/
filesystem.h
355 lines (311 loc) · 13.2 KB
/
filesystem.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
/*
* The Doomsday Engine Project -- libcore
*
* Copyright © 2009-2013 Jaakko Keränen <jaakko.keranen@iki.fi>
*
* @par License
* LGPL: http://www.gnu.org/licenses/lgpl.html
*
* <small>This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at your
* option) any later version. This program is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details. You should have received a copy of
* the GNU Lesser General Public License along with this program; if not, see:
* http://www.gnu.org/licenses</small>
*/
#ifndef LIBDENG2_FILESYSTEM_H
#define LIBDENG2_FILESYSTEM_H
#include "../libcore.h"
#include "../Folder"
#include "../FileIndex"
#include "../System"
#include "IInterpreter"
#include <QFlags>
#include <functional>
/**
* @defgroup fs File System
*
* The file system (de::FileSystem) governs a tree of files and folders, and
* provides the means to access all data in libcore. It follows the metaphor
* of a UNIX file system, where not all files are "regular" files, but instead
* may represent non-file objects that still support serialization into byte
* arrays or have a byte-stream input/output interface. This way it provides a
* uniform interface to all public data that is compatible with network
* communications, persistence, hierarchical organization and lookup, item
* metadata (names, modification timestamps, custom key/values) and scripting.
*
* To facilitate efficient O(log n) searches over the entire file system,
* de::FileSystem maintains an index of all files and folders by name. There is
* additionally a separate index for each file type (e.g., de::ArchiveEntryFile).
*
* The file system has to be manually refreshed when the underlying data
* changes. For instance, when new files are written to a folder on the hard
* drive, one must call de::FileSystem::refresh() for the changes to be reflected
* in the de::FileSystem index and tree.
*
* ZIP (PK3) archives are visible in the libcore file system as Folder and
* File instances just like regular native files are. This allows one to deploy
* a large collection of resources as an archive and treat it at runtime just
* like a tree of native files. Files within archives can be read and written
* just like native files, and the containing archives will be updated as
* needed.
* @see de::ArchiveEntryFile, de::ArchiveFeed, and de::FileSystem::interpret()
*/
namespace de {
namespace internal {
template <typename Type>
inline bool cannotCastFileTo(File *file) {
return dynamic_cast<Type *>(file) == NULL;
}
}
/**
* The file system maintains a tree of files and folders. It provides a way
* to quickly and efficiently locate files anywhere in the tree. It also
* maintains semantic information about the structure and content of the
* file tree, allowing others to know how to treat the files and folders.
*
* In practice, the file system consists of a tree of File and Folder
* instances. These instances are generated by the Feed objects attached to
* the folders. For instance, a DirectoryFeed will generate the appropriate
* File and Folder instances for a directory in the native file system.
*
* Wildcard searches are discouraged as implementing them is potentially
* inefficient. Instead, suitable indices should be built beforehand if
* there is a need to look up lots of files matching a specific criteria
* from unknown locations in the tree.
*
* The file system can be repopulated at any time to resynchronize it with
* the source data. Repopulation is nondestructive as long as the source
* data has not changed. Repopulation is needed for instance when native
* files get deleted in the directory a folder is feeding on. The feeds are
* responsible for deciding when instances get out-of-date and need to be
* deleted (pruning). Pruning occurs when a folder that is already
* populated with files is repopulated.
*
* @ingroup fs
*/
class DENG2_PUBLIC FileSystem : public System
{
public:
/// No index is found for the specified type. @ingroup errors
DENG2_ERROR(UnknownTypeError);
/// No files found. @ingroup errors
DENG2_ERROR(NotFoundError);
/// More than one file found and there is not enough information to choose
/// between them. @ingroup errors
DENG2_ERROR(AmbiguousError);
typedef FileIndex Index;
typedef FileIndex::FoundFiles FoundFiles;
public:
/**
* Constructs a new file system. The file system needs to be manually
* refreshed; initially it is empty.
*/
FileSystem();
/**
* Registers a new file content interpreter.
*
* A file interpreter takes a "raw" file (e.g., byte array) and provides
* access to the file contents in a high-level manner (e.g., an Image).
* Registered interpreters get used automatically when feeds populate
* folders with files.
*
* All registered interpreters are consulted in last-to-first order.
*
* @param interpreter Interpreter object. Ownership not taken.
*/
void addInterpreter(filesys::IInterpreter const &interpreter);
void printIndex();
Folder &root();
Folder const &root() const;
/**
* Refresh the file system. Populates all folders with files from the feeds.
*/
void refresh();
enum FolderCreationBehavior {
DontInheritFeeds = 0, ///< Subfolder will not have any feeds created for them.
InheritPrimaryFeed = 0x1, ///< Subfolder will inherit the primary (first) feed of its parent.
InheritAllFeeds = 0x2, ///< Subfolder will inherit all feeds of its parent.
PopulateNewFolder = 0x4, ///< Populate new folder automatically.
InheritPrimaryFeedAndPopulate = InheritPrimaryFeed | PopulateNewFolder
};
Q_DECLARE_FLAGS(FolderCreationBehaviors, FolderCreationBehavior)
/**
* Retrieves a folder in the file system. The folder gets created if it
* does not exist. Any missing parent folders will also be created.
*
* @param path Path of the folder. Relative to the root folder.
* @param behavior What to do with the new folder: automatically attach feeds and
* maybe also populate it automatically.
*
* @return Folder at @a path.
*/
Folder &makeFolder(String const &path,
FolderCreationBehaviors behavior = InheritPrimaryFeedAndPopulate);
/**
* Retrieves a folder in the file system and replaces all of its existing
* feeds with the specified feed. The folder gets created if it does not
* exist. If it does exist, the folder will be cleared so that any existing
* contents won't be orphaned due to the previous feeds going away.
*
* Any missing parent folders will also be created.
*
* @param path Path of the folder. Relative to the root folder.
* @param feed Primary feed for the folder (other feeds removed).
* @param behavior Behavior for creating folders (including missing parents).
* @param populationBehavior How to populate the returned folder (if population requested).
*
* @return Folder at @a path.
*/
Folder &makeFolderWithFeed(String const &path, Feed *feed,
Folder::PopulationBehavior populationBehavior = Folder::PopulateFullTree,
FolderCreationBehaviors behavior = InheritPrimaryFeedAndPopulate);
/**
* Finds all files matching a full or partial path. The search is done
* using the file system's index; no recursive descent into folders is
* done.
*
* @param partialPath Partial path or file name to look for.
* @param found Set of files that match the result.
*
* @return Number of files found.
*/
int findAll(String const &partialPath, FoundFiles &found) const;
LoopResult forAll(String const &partialPath, std::function<LoopResult (File &)> func);
template <typename Predicate>
int findAll(Predicate exclusion, String const &partialPath, FoundFiles &found) const {
findAll(partialPath, found);
found.remove_if(exclusion);
return int(found.size());
}
int findAllOfType(String const &typeIdentifier, String const &path, FoundFiles &found) const;
LoopResult forAllOfType(String const &typeIdentifier, String const &path,
std::function<LoopResult (File &)> func);
int findAllOfTypes(StringList const &typeIdentifiers, String const &path, FoundFiles &found) const;
/**
* Finds a single file matching a full or partial path. The search is
* done using the file system's index; no recursive descent into
* folders is done.
*
* @param path Path or file name to look for.
*
* @return The found file.
*/
File &find(String const &path) const;
/**
* Finds a file of a specific type. The search is done using the file
* system's index; no recursive descent into folders is done. Only
* files that can be represented as @a Type are included in the
* results.
*
* @param path Full/partial path or file name to look for.
*
* @see indexFor() returns the full index for a particular type of file
* for manual searches.
*/
template <typename Type>
Type &find(String const &path) const {
FoundFiles found;
// Filter out the wrong types.
findAll(internal::cannotCastFileTo<Type>, path, found);
if(found.size() > 1) {
/// @throw AmbiguousError More than one file matches the conditions.
throw AmbiguousError("FS::find", "More than one file found matching '" + path + "'");
}
if(found.empty()) {
/// @throw NotFoundError No files found matching the condition.
throw NotFoundError("FS::find", "No files found matching '" + path + "'");
}
return *dynamic_cast<Type *>(found.front());
}
/**
* Creates an interpreter for the data in a file.
*
* @param sourceData File with the source data. While interpreting,
* ownership of the file is given to de::FileSystem.
*
* @return If the format of the source data was recognized, returns a new
* File (or Folder) that can be used for accessing the data. Caller gets
* ownership of the returned instance. Ownership of the @a sourceData will
* be transferred to the returned instance. If the format was not
* recognized, @a sourceData is returned as-is and ownership is returned to
* the caller.
*/
File *interpret(File *sourceData);
/**
* Provides access to the main index of the file system. This can be
* used for efficiently looking up files based on name.
*
* @note The file names are indexed in lower case.
*/
FileIndex const &nameIndex() const;
/**
* Retrieves the index of files of a particular type.
*
* @param typeIdentifier Type identifier to look for. Use the DENG2_TYPE_NAME() macro.
*
* @return A subset of the main index containing only the entries of
* the given type.
*
* For example, to look up the index for NativeFile instances:
* @code
* FileIndex const &nativeFileIndex = App::fileSystem().indexFor(DENG2_TYPE_NAME(NativeFile));
* @endcode
*/
FileIndex const &indexFor(String const &typeIdentifier) const;
/**
* Adds a new custom index to the file system.
*
* @param userIndex Index where files will be included. Ownership not taken;
* index must exist until removed from use.
*/
void addUserIndex(FileIndex &userIndex);
/**
* Removes a custom index from the file system.
*
* @param userIndex
*/
void removeUserIndex(FileIndex &userIndex);
/**
* Adds a file to the main index.
*
* @param file File to index.
*/
void index(File &file);
/**
* Removes a file from the main index.
*
* @param file File to remove from the index.
*/
void deindex(File &file);
enum CopyBehavior
{
PlainFileCopy = 0,
ReinterpretDestination = 0x1,
PopulateDestination = 0x2,
DefaultCopyBehavior = ReinterpretDestination | PopulateDestination
};
Q_DECLARE_FLAGS(CopyBehaviors, CopyBehavior)
/**
* Makes a copy of a file by streaming the bytes of the source path to the
* destination path.
*
* @param sourcePath Source path.
* @param destinationPath Destination path.
* @param behavior Copy behavior: which members to copy.
*/
File ©Serialized(String const &sourcePath, String const &destinationPath,
CopyBehaviors behavior = DefaultCopyBehavior);
void timeChanged(Clock const &);
private:
DENG2_PRIVATE(d)
};
Q_DECLARE_OPERATORS_FOR_FLAGS(FileSystem::FolderCreationBehaviors)
Q_DECLARE_OPERATORS_FOR_FLAGS(FileSystem::CopyBehaviors)
// Alias.
typedef FileSystem FS;
} // namespace de
#endif // LIBDENG2_FILESYSTEM_H