File.hpp
Go to the documentation of this file.
1 // @formatter:off
2 //
3 // Balau core C++ library
4 //
5 // Copyright (C) 2008 Bora Software (contact@borasoftware.com)
6 //
7 // Licensed under the Boost Software License - Version 1.0 - August 17th, 2003.
8 // See the LICENSE file for the full license text.
9 //
10 
16 
17 #ifndef COM_BORA_SOFTWARE__BALAU_RESOURCE__FILE
18 #define COM_BORA_SOFTWARE__BALAU_RESOURCE__FILE
19 
25 
26 #include <Balau/Util/Strings.hpp>
27 
28 #include <chrono>
29 
30 namespace Balau::Resource {
31 
35 class File : public Uri {
39  public: class RecursiveFileIterator {
43  public: bool hasNext() const {
44  return iterator != end;
45  }
46 
50  public: File next() {
51  auto file = File(*iterator);
52  ++iterator;
53  return file;
54  }
55 
56  friend class File;
57 
58  private: explicit RecursiveFileIterator(const File & file)
59  : iterator(boost::filesystem::recursive_directory_iterator(file.entry)) {}
60 
61  private: boost::filesystem::recursive_directory_iterator iterator;
62  private: boost::filesystem::recursive_directory_iterator end;
63  };
64 
72  public: bool hasNext() const override {
73  return iterator != end;
74  }
75 
79  public: std::unique_ptr<Uri> next() override {
80  boost::filesystem::directory_entry e = *iterator;
81  ++iterator;
82  return std::unique_ptr<Uri>(new File(e));
83  }
84 
85  friend class File;
86 
87  private: explicit RecursiveFileUriIterator(const File & file)
88  : iterator(boost::filesystem::recursive_directory_iterator(file.entry)) {}
89 
90  private: boost::filesystem::recursive_directory_iterator iterator;
91  private: boost::filesystem::recursive_directory_iterator end;
92  };
93 
97  public: class FileIterator {
101  public: bool hasNext() const {
102  return iterator != end;
103  }
104 
108  public: File next() {
109  auto file = File(*iterator);
110  ++iterator;
111  return file;
112  }
113 
114  friend class File;
115 
116  private: explicit FileIterator(const File & file)
117  : iterator(boost::filesystem::directory_iterator(file.entry)) {}
118 
119  private: boost::filesystem::directory_iterator iterator;
120  private: boost::filesystem::directory_iterator end;
121  };
122 
126  private: class FileUriIterator : public UriIterator {
130  public: bool hasNext() const override {
131  return iterator != end;
132  }
133 
137  public: std::unique_ptr<Uri> next() override {
138  boost::filesystem::directory_entry e = *iterator;
139  ++iterator;
140  return std::unique_ptr<Uri>(new File(e));
141  }
142 
143  friend class File;
144 
145  private: explicit FileUriIterator(const File & file)
146  : iterator(boost::filesystem::directory_iterator(file.entry)) {}
147 
148  private: boost::filesystem::directory_iterator iterator;
149  private: boost::filesystem::directory_iterator end;
150  };
151 
155  public: static const boost::filesystem::path::value_type separator = boost::filesystem::path::preferred_separator;
156 
160  public: File() = default;
161 
165  public: explicit File(boost::filesystem::directory_entry entry_) : entry(std::move(entry_)) {}
166 
170  public: explicit File(const boost::filesystem::path & entry_) : entry(entry_) {}
171 
177  public: explicit File(const std::string & path) : entry(boost::filesystem::directory_entry(path)) {}
178 
184  public: explicit File(const char * path) : entry(boost::filesystem::directory_entry(path)) {}
185 
191  public: explicit File(std::string_view path) : entry(boost::filesystem::directory_entry(std::string(path))) {}
192 
198  public: File(const std::string & path, const std::string & name) : entry(create(path, name)) {}
199 
205  public: File(const File & path, const std::string & name) : entry(append(path.entry, name)) {}
206 
210  public: File(File && file) noexcept : entry(std::move(file.entry)) {}
211 
215  public: File(const File & file) : entry(file.entry) {}
216 
217  public: std::unique_ptr<Uri> clone() const override {
218  return std::unique_ptr<Uri>(new File(*this));
219  }
220 
221  public: std::unique_ptr<Uri> append(const std::string & pathComponent) const override {
222  return std::unique_ptr<Uri>(new File(*this / pathComponent));
223  }
224 
225  public: std::unique_ptr<Uri> resolve(std::string_view path) const override {
226  static const std::regex scheme { "[a-zA-Z][a-zA-Z0-9+-\\.]*:" };
227 
228  auto cleanPath = Util::Strings::trim(path);
229  auto str = std::string(cleanPath);
230  std::string sPath;
231  bool hasFileScheme = false;
232 
233  if (Util::Strings::startsWith(str, "file:")) {
234  // Prefixed with file schema.
235  // Relative or absolute?
236  sPath = str.substr(5);
237  hasFileScheme = true;
238  } else if (Util::Strings::startsWithRegex(str, scheme)) {
239  std::unique_ptr<Uri> uri;
240  fromString(uri, str);
241  return uri;
242  } else {
243  // No scheme prefix.
244  // Absolute or relative file path.
245  sPath = str;
246  }
247 
248  if ((!hasFileScheme && sPath.empty()) || (hasFileScheme && sPath == "//")) {
249  // Empty.. return the path if it is a folder,
250  // otherwise return the parent path of the file.
251 
252  if (isRegularDirectory()) {
253  return std::unique_ptr<Uri>(new File(*this));
254  } else {
255  return std::unique_ptr<Uri>(new File(getParentDirectory()));
256  }
257  } else if (sPath[0] == '/') {
258  // Absolute.
259 
260  if (hasFileScheme) {
261  // Invalid?
262  if (sPath.length() < 3 || sPath.substr(0, 3) != "///") {
264 
266  Exception::IllegalArgumentException, toString("Illegal path string in file URI: ", path)
267  );
268  }
269 
270  // Strip the leading double slash "//".
271  sPath = sPath.substr(2);
272  }
273 
274  return std::unique_ptr<Uri>(new File(sPath));
275  } else {
276  // Relative.. resolve according to the current path
277  // (current file if folder or parent otherwise).
278 
279  auto bPath = boost::filesystem::path(sPath);
280 
281  if (isRegularDirectory()) {
282  auto newPath = (getEntry() / bPath).lexically_normal();
283  return std::unique_ptr<Uri>(new File(newPath));
284  } else {
285  auto newPath = (getParentDirectory().getEntry() / bPath).lexically_normal();
286  return std::unique_ptr<Uri>(new File(newPath));
287  }
288  }
289  }
290 
291  public: bool canReadFrom() const override {
292  return true;
293  }
294 
295  public: bool canWriteTo() const override {
296  return true;
297  }
298 
299  public: std::unique_ptr<ByteReadResource> byteReadResource() const override {
300  return std::unique_ptr<ByteReadResource>(new FileByteReadResource(*this));
301  }
302 
303  public: std::unique_ptr<Utf8To32ReadResource> utf8To32ReadResource() const override {
304  return std::unique_ptr<Utf8To32ReadResource>(new FileUtf8To32ReadResource(*this));
305  }
306 
307  public: std::unique_ptr<ByteWriteResource> byteWriteResource() override {
308  return std::unique_ptr<ByteWriteResource>(new FileByteWriteResource(*this));
309  }
310 
311  public: std::unique_ptr<Utf32To8WriteResource> utf32To8WriteResource() override {
312  return std::unique_ptr<Utf32To8WriteResource>(new FileUtf32To8WriteResource(*this));
313  }
314 
321  return FileByteReadResource(*this);
322  }
323 
330  return FileUtf8To32ReadResource(*this);
331  }
332 
339  return FileByteWriteResource(*this);
340  }
341 
348  return FileUtf32To8WriteResource(*this);
349  }
350 
351  public: bool isRecursivelyIterable() const override {
352  return isRegularDirectory();
353  }
354 
355  public: bool isIterable() const override {
356  return isRegularDirectory();
357  }
358 
359  public: std::unique_ptr<RecursiveUriIterator> recursiveIterator() const override {
360  if (!isRegularDirectory()) {
361  ThrowBalauException(Exception::NotImplementedException, "Plain files do not have a recursive iterator.");
362  }
363 
364  return std::unique_ptr<RecursiveUriIterator>(new RecursiveFileUriIterator(*this));
365  }
366 
367  public: std::unique_ptr<UriIterator> iterator() const override {
368  if (!isRegularDirectory()) {
369  ThrowBalauException(Exception::NotImplementedException, "Plain files do not have an iterator.");
370  }
371 
372  return std::unique_ptr<UriIterator>(new FileUriIterator(*this));
373  }
374 
382  return RecursiveFileIterator(*this);
383  }
384 
391  public: virtual FileIterator fileIterator() const {
392  return FileIterator(*this);
393  }
394 
400  public: boost::filesystem::directory_entry getEntry() const {
401  return entry;
402  }
403 
409  public: File & operator = (const File & file) {
410  entry = file.entry;
411  return *this;
412  }
413 
419  public: File & operator = (File && file) noexcept {
420  entry = std::move(file.entry);
421  return *this;
422  }
423 
429  public: File & operator = (const boost::filesystem::directory_entry & entry_) {
430  entry = entry_;
431  return *this;
432  }
433 
439  public: File & operator = (const boost::filesystem::path & path) {
440  entry = boost::filesystem::directory_entry(path);
441  return *this;
442  }
443 
449  public: bool exists() const {
450  return boost::filesystem::exists(entry);
451  }
452 
458  public: size_t size() const {
459  boost::system::error_code errorCode;
460  boost::uintmax_t sz = boost::filesystem::file_size(entry, errorCode);
461 
462  if (errorCode) {
465  , std::string("An error occurred when trying to get the file size: ") + errorCode.message()
466  );
467  }
468 
469  return sz;
470  }
471 
477  public: std::chrono::system_clock::time_point getModifiedTimestamp() const {
478  return std::chrono::system_clock::from_time_t(boost::filesystem::last_write_time(entry));
479  }
480 
486  public: bool isRegularDirectory() const override {
487  return boost::filesystem::is_directory(entry);
488  }
489 
495  public: bool isRegularFile() const override {
496  return boost::filesystem::is_regular_file(entry);
497  }
498 
504  public: File getParentDirectory() const {
505  return File(boost::filesystem::directory_entry(entry.path().parent_path()));
506  }
507 
513  public: File getSubDirectory(std::string subDirectory) const {
514  return File(
515  boost::filesystem::directory_entry(boost::filesystem::path(entry.path()) /= subDirectory)
516  );
517  }
518 
522  public: File getChildEntry(std::string child) const {
523  boost::filesystem::path p = entry.path();
524  p /= child;
525  return File(boost::filesystem::directory_entry(p));
526  }
527 
537  public: bool createDirectories() const {
538  return boost::filesystem::create_directories(entry);
539  }
540 
546  public: bool removeFile() const {
547  return boost::filesystem::remove(entry);
548  }
549 
555  public: File toAbsolutePath() const {
556  return File(boost::filesystem::absolute(entry));
557  }
558 
564  public: File relative(const File & base) const {
565  return File(boost::filesystem::relative(entry, base.entry));
566  }
567 
568  public: std::string toUriString() const override {
569  return std::string("file://") + Util::Strings::replaceAll(entry.path().string(), "\\", "/");
570  }
571 
572  public: std::string toRawString() const override {
573  return entry.path().string();
574  }
575 
576  public: template <typename AllocatorT> Balau::U8String<AllocatorT> toRawString() const {
577  return ::toString<AllocatorT>(entry.path().string());
578  }
579 
580  public: size_t hashcode() const noexcept override {
581  return std::hash<std::string>()(entry.path().string());
582  }
583 
589  public: bool operator == (const File & rhs) const {
590  return entry == rhs.entry;
591  }
592 
593  public: bool operator == (const Uri & rhs) const override {
594  const auto * o = dynamic_cast<const File *>(&rhs);
595  return o == nullptr ? false : entry == o->entry;
596  }
597 
603  public: bool operator < (const File & rhs) const {
604  return entry < rhs.entry;
605  }
606 
612  public: File operator / (const char * component) const {
613  return File(append(entry, component));
614  }
615 
621  public: File operator / (const std::string & component) const {
622  return File(append(entry, component));
623  }
624 
630  public: File operator / (const std::string_view & component) const {
631  return File(append(entry, std::string(component)));
632  }
633 
642  public: template <typename ... T, template <typename ...> class Container>
643  File operator / (const Container<T ...> & container) const {
645 
646  boost::filesystem::path p = entry.path();
647 
648  for (const auto & component : container) {
649  p /= toString(component);
650  }
651 
652  return File(boost::filesystem::directory_entry(p));
653  }
654 
660  public: File & operator /= (const char * component) {
661  entry = append(entry, component);
662  return *this;
663  }
664 
670  public: File & operator /= (const std::string & component) {
671  entry = append(entry, component);
672  return *this;
673  }
674 
680  public: File & operator /= (const std::string_view & component) {
681  entry = append(entry, std::string(component));
682  return *this;
683  }
684 
690  public: File operator + (const std::string & fragment) const {
691  boost::filesystem::path p = entry.path();
692  p += fragment;
693  return File(boost::filesystem::directory_entry(p));
694  }
695 
696  public: void dispatch(UriDispatcher & dispatcher) const override {
697  dispatcher.dispatch(*this);
698  }
699 
701 
702  protected: boost::filesystem::directory_entry entry;
703 
704  private: static boost::filesystem::directory_entry create(const std::string & path,
705  const std::string & name) {
706  boost::filesystem::directory_entry ret(path);
707  return append(ret, name);
708  }
709 
710  private: static boost::filesystem::directory_entry append(const boost::filesystem::directory_entry & de,
711  const std::string & name) {
712  boost::filesystem::path p = de.path();
713  p /= name;
714  return boost::filesystem::directory_entry(p);
715  }
716 };
717 
723 template <typename AllocatorT>
725  return file.toRawString<AllocatorT>();
726 }
727 
733 inline void fromString(File & destination, std::string_view value) {
734  destination = File(value);
735 }
736 
737 } // namespace Balau::Resource
738 
739 namespace std { // NOLINT
740 
741 template <> struct hash<Balau::Resource::File> {
742  size_t operator () (const Balau::Resource::File & file) const noexcept {
743  return file.hashcode();
744  }
745 };
746 
747 template <> struct equal_to<Balau::Resource::File> {
748  bool operator () (const Balau::Resource::File & lhs, const Balau::Resource::File & rhs) const {
749  return lhs == rhs;
750  }
751 };
752 
753 } // namespace std
754 
755 #endif // COM_BORA_SOFTWARE__BALAU_RESOURCE__FILE
std::unique_ptr< Utf32To8WriteResource > utf32To8WriteResource() override
Get a UTF-32 to UTF-8 write resource for the URI.
Definition: File.hpp:311
std::unique_ptr< UriIterator > iterator() const override
Get a (non-recursive) iterator.
Definition: File.hpp:367
bool isIterable() const override
Does the URI have a non-recursive iterator (examples: file and zip archive URIs). ...
Definition: File.hpp:355
A write-only file based UTF-8 resource which is written as UTF-32 characters.
File operator/(const char *component) const
Append a path component to the path represented by the file.
Definition: File.hpp:612
File(std::string_view path)
Create a file path with the supplied path string.
Definition: File.hpp:191
virtual void dispatch(const File &object)=0
Visit a File URI.
std::string toUriString() const override
Get a string representing the URI, complete with scheme.
Definition: File.hpp:568
FileByteWriteResource getByteWriteResource() const
Get a file byte write resource for this file URI.
Definition: File.hpp:338
bool removeFile() const
Remove the file if it exists.
Definition: File.hpp:546
Balau exceptions for I/O.
File(const File &path, const std::string &name)
Create a file path with the supplied path and filename.
Definition: File.hpp:205
Definition: NetworkTypes.hpp:39
bool isRecursivelyIterable() const override
Does the URI have a recursive iterator (examples: file and zip archive URIs).
Definition: File.hpp:351
#define ThrowBalauException(ExceptionClass,...)
Throw a Balau style exception, with implicit file and line number, and optional stacktrace.
Definition: BalauException.hpp:45
Thrown when an IO exception occurs.
Definition: IOExceptions.hpp:28
virtual RecursiveFileIterator recursiveFileIterator() const
Get a recursive file iterator for this file (directory).
Definition: File.hpp:381
size_t size() const
Get the size of the file if it exists or -1 if the file does not exist.
Definition: File.hpp:458
Recursive iteration into a directory structure.
Definition: File.hpp:39
void fromString(File &destination, std::string_view value)
Overwrite the supplied file URI by assignment by converting the supplied UTF-8 string to a file URI...
Definition: File.hpp:733
File(File &&file) noexcept
Move an existing file URI into a new File URI.
Definition: File.hpp:210
std::unique_ptr< Uri > resolve(std::string_view path) const override
Resolve the relative or absolute path, in reference to the current URI.
Definition: File.hpp:225
A write only standard file on a file system which is written as bytes.
An abstract universal resource identifier.
Definition: Uri.hpp:131
std::unique_ptr< Uri > next() override
Get the next item in the iterator.
Definition: File.hpp:79
File(const std::string &path)
Create a file path with the supplied path string.
Definition: File.hpp:177
bool hasNext() const
Returns true if there is another item available in the iterator.
Definition: File.hpp:101
STL namespace.
std::unique_ptr< Uri > next() override
Get the next item in the iterator.
Definition: File.hpp:137
std::unique_ptr< Uri > clone() const override
Clone the concrete Uri.
Definition: File.hpp:217
static std::basic_string< CharT > replaceAll(const StringT< CharT, T ... > &input, const MatchT &match, const ReplacementT &replacement)
Replace all occurrences of the specified string with the supplied replacement.
Definition: Strings.hpp:844
A write only standard file on a file system which is written as bytes.
Definition: FileByteWriteResource.hpp:32
File & operator/=(const char *component)
Append a path component to the path represented by the file in place.
Definition: File.hpp:660
A write-only UTF-8 resource in a standard file on a file system, which is written with UTF-32 charact...
Definition: FileUtf32To8WriteResource.hpp:32
File(const char *path)
Create a file path with the supplied path string.
Definition: File.hpp:184
bool operator==(const File &rhs) const
Compare the supplied file to the current file.
Definition: File.hpp:589
File getParentDirectory() const
Get the parent directory of the file URI.
Definition: File.hpp:504
FileByteReadResource getByteReadResource() const
Get a file byte read resource for this file URI.
Definition: File.hpp:320
The unified resource class hierarchy.
Definition: ByteReadResource.hpp:24
A read-only standard file on a file system which is read as bytes.
Thrown when a feature is not yet implemented.
Definition: BalauException.hpp:162
An abstract read iterator that iterates recursively.
Definition: Uri.hpp:37
A read-only standard file on a file system which is read as bytes.
Definition: FileByteReadResource.hpp:32
Non-recursive iteration over a directory&#39;s contents.
Definition: File.hpp:97
File next()
Get the next item in the iterator.
Definition: File.hpp:108
File(const std::string &path, const std::string &name)
Create a file path with the supplied path string and filename string.
Definition: File.hpp:198
bool createDirectories() const
Create all intermediate directories and the final directory.
Definition: File.hpp:537
File(boost::filesystem::directory_entry entry_)
Create a file from the supplied directory entry.
Definition: File.hpp:165
Non-recursive iteration over a directory&#39;s contents (URI version).
Definition: File.hpp:126
A read-only file based UTF-8 resource which is read as UTF-32 characters.
Definition: FileUtf8To32ReadResource.hpp:32
Thrown when an illegal argument is passed to a function or method.
Definition: BalauException.hpp:138
Balau::U8String< AllocatorT > toString(const File &file)
Print the file URI as a UTF-8 string.
Definition: File.hpp:724
File(const File &file)
Copy an existing file URI into a new File URI.
Definition: File.hpp:215
Utilities for strings.
boost::filesystem::directory_entry getEntry() const
Get the underlying directory entry for this file URI.
Definition: File.hpp:400
bool isRegularFile() const override
Returns true if the file URI points to a regular file.
Definition: File.hpp:495
static bool startsWith(const StringT< CharT, T ... > &str, const PrefixT &prefix)
Does the string start with the specified prefix?
Definition: Strings.hpp:46
static std::string_view trim(const std::string_view &input)
Trim whitespace from the beginning and end of the supplied UTF-8 string.
Definition: Strings.hpp:706
bool hasNext() const override
Returns true if there is another item available in the iterator.
Definition: File.hpp:72
virtual FileIterator fileIterator() const
Get a non-recursive file iterator for this file (directory).
Definition: File.hpp:391
void dispatch(UriDispatcher &dispatcher) const override
Visitor pattern dispatching.
Definition: File.hpp:696
const std::string message
The message.
Definition: BalauException.hpp:59
bool hasNext() const override
Returns true if there is another item available in the iterator.
Definition: File.hpp:130
File toAbsolutePath() const
Convert the file to an absolute path if it is relative, using the current working directory...
Definition: File.hpp:555
A file on the local file system.
Definition: File.hpp:35
File relative(const File &base) const
Get the relative path of the current object, compared to the supplied file.
Definition: File.hpp:564
File & operator=(const File &file)
Copy assign the supplied file to this file.
Definition: File.hpp:409
File(const boost::filesystem::path &entry_)
Create a file from the supplied Boost path.
Definition: File.hpp:170
std::unique_ptr< Utf8To32ReadResource > utf8To32ReadResource() const override
Get a UTF-8 to UTF-32 read resource for the URI.
Definition: File.hpp:303
std::basic_string< char, std::char_traits< char >, AllocatorT > U8String
UTF-8 string type with selectable allocator.
Definition: ToStringA.hpp:41
bool isRegularDirectory() const override
Returns true if the file URI points to a directory.
Definition: File.hpp:486
Recursive iteration into a directory structure (URI version).
Definition: File.hpp:68
FileUtf8To32ReadResource getUtf8To32ReadResource() const
Get a file UTF-8 to UTF-32 read resource for this file URI.
Definition: File.hpp:329
std::string toRawString() const override
Get a string representing the raw URI.
Definition: File.hpp:572
static bool startsWithRegex(const std::string &str, const std::regex &prefix)
Does the string start with the specified regular expression?
Definition: Strings.hpp:94
std::unique_ptr< ByteReadResource > byteReadResource() const override
Get a byte read resource for the URI.
Definition: File.hpp:299
std::chrono::system_clock::time_point getModifiedTimestamp() const
Get the modified time of the file.
Definition: File.hpp:477
std::unique_ptr< Uri > append(const std::string &pathComponent) const override
Appends the path component to the supplied URI, returning a new URI.
Definition: File.hpp:221
Balau::U8String< AllocatorT > toRawString() const
Get a string representing the raw URI.
Definition: File.hpp:576
static const boost::filesystem::path::value_type separator
The path separator character for the platform.
Definition: File.hpp:155
size_t hashcode() const noexcept override
Get the URI&#39;s hash code.
Definition: File.hpp:580
File getChildEntry(std::string child) const
Definition: File.hpp:522
bool canReadFrom() const override
Can data be read from the URI via a read resource.
Definition: File.hpp:291
FileUtf32To8WriteResource getUtf32To8WriteResource() const
Get a file UTF-8 to UTF-32 write resource for this file URI.
Definition: File.hpp:347
An abstract read iterator.
Definition: Uri.hpp:84
File operator+(const std::string &fragment) const
Concatenate the supplied path fragment to the end of the path represented by the file.
Definition: File.hpp:690
bool exists() const
Returns true if an item exits in the file system for the file URI.
Definition: File.hpp:449
std::unique_ptr< RecursiveUriIterator > recursiveIterator() const override
Get a recursive iterator.
Definition: File.hpp:359
A read-only file based UTF-8 resource which is read as UTF-32 characters.
File next()
Get the next item in the iterator.
Definition: File.hpp:50
std::unique_ptr< ByteWriteResource > byteWriteResource() override
Get a byte write resource for the URI.
Definition: File.hpp:307
bool operator<(const File &rhs) const
Return true if the current object is less than the supplied instance.
Definition: File.hpp:603
bool canWriteTo() const override
Can data be written to the URI via a write resource.
Definition: File.hpp:295
Visitor interface for URIs.
Definition: UriDispatcher.hpp:32
bool hasNext() const
Returns true if there is another item available in the iterator.
Definition: File.hpp:43
File getSubDirectory(std::string subDirectory) const
Create a new file representing the specified sub-directory of the file URI.
Definition: File.hpp:513