2.6: Looking at FileSystem Internals
- Page ID
- 43797
At that stage, you should be able to comfortably use FileSystem to cover your need file handing. This section is about the internal components of FileSystem. It goes over important implementation details, which will surely interest readers willing to have a new kind of file system, for example on a data base or a remote file system.
FileReference = FileSystem + Path
Paths and filesystems are the lowest level of the FileSystem API. A FileReference
combines a path and a filesystem into a single object which provides a simpler protocol for working with files as we show in the previous section. References implement the path protocol with methods like /
, parent
and resolve:
.
FileSystem
A filesystem is an interface to access hierarchies of directories and files. "The filesystem," provided by the host operating system, is represented by DiskStore
and its platform-specific subclasses. However, the user should not access them directly but instead use FileSystem
as we showed previously. Other kinds of filesystems are also possible. The memory filesystem provides a RAM disk filesystem where all files are stored as ByteArrays in the image. The zip filesystem represents the contents of a zip file.
Each filesystem has its own working directory, which is used to resolve any relative paths that are passed to it. Some examples:
fs := FileSystem memory. fs workingDirectoryPath: (Path / 'plonk'). griffle := Path / 'plonk' / 'griffle'. nurp := Path * 'nurp'. fs resolve: nurp. → Path/plonk/nurp fs createDirectory: (Path / 'plonk'). → "/plonk created" (fs writeStreamOn: griffle) close. → "/plonk/griffle created" fs isFile: griffle. → true fs isDirectory: griffle. → false fs copy: griffle to: nurp. → "/plonk/griffle copied to /plonk/nurp" fs exists: nurp. → true fs delete: griffle. → "/plonk/griffle" deleted fs isFile: griffle. → false fs isDirectory: griffle. → false
Path
Paths are the most fundamental element of the FileSystem API. They represent filesystem paths in a very abstract sense, and provide a high-level protocol for working with paths without having to manipulate strings. Here are some examples showing how to define absolute paths (/
), relative paths (*
), file extension (,
), parent navigation (parent
). Normally you do not need to use Path but here are some examples.
| fs griffle nurp | fs := FileSystem memory. griffle := fs referenceTo: (Path / 'plonk' / 'griffle'). nurp := fs referenceTo: (Path * 'nurp'). griffle isFile. → false griffle isDirectory. → false griffle parent ensureCreateDirectory. griffle ensureCreateFile. griffle exists & griffle isFile. → true griffle copyTo: nurp. nurp exists. → true griffle delete
"absolute path" Path / 'plonk' / 'feep' → /plonk/feep "relative path" Path * 'plonk' / 'feep' → plonk/feep "relative path with extension" Path * 'griffle' , 'txt' → griffle.txt "changing the extension" Path * 'griffle.txt' , 'jpeg' → griffle.jpeg "parent directory" (Path / 'plonk' / 'griffle') parent → /plonk "resolving a relative path" (Path / 'plonk' / 'griffle') resolve: (Path * '..' / 'feep') → /plonk/feep "resolving an absolute path" (Path / 'plonk' / 'griffle') resolve: (Path / 'feep') → /feep "resolving a string" (Path * 'griffle') resolve: 'plonk' → griffle/plonk "comparing" (Path / 'plonk') contains: (Path / 'griffle' / 'nurp') → false
Note that some of the path protocol (messages like /
, parent
and resolve:
) are also available on references.
Visitors
The above methods are sufficient for many common tasks, but application developers may find that they need to perform more sophisticated operations on directory trees.
The visitor protocol is very simple. A visitor needs to implement visitFile:
and visitDirectory:
. The actual traversal of the filesystem is handled by a guide. A guide works with a visitor, crawling the filesystem and notifying the visitor of the files and directories it discovers. There are three Guide classes, PreorderGuide
, PostorderGuide
and BreadthFirstGuide
, which traverse the filesystem in different orders. To arrange for a guide to traverse the filesystem with a particular visitor is simple. Here’s an example:
BreadthFirstGuide show: aReference to: aVisitor
The enumeration methods described above are implemented with visitors; see CopyVisitor
, DeleteVisitor
, and CollectVisitor
for examples.