Design Pattern Examples
Overview of object-oriented design patterns
Composite_FileAccess.c
Go to the documentation of this file.
1
6
7#include <stdlib.h>
8#include <string.h>
9#include <time.h>
10
11#include "helpers/datetime.h"
12#include "helpers/replace.h"
13#include "helpers/split.h"
14#include "helpers/strdup.h"
15
18
22static FileDirEntry* _root = NULL;
23
24
34FileDirEntry* _NewFileEntry(const char* name, long length, time_t whenModified)
35{
36 FileEntry* fileEntry = calloc(1, sizeof(FileEntry));
37 if (fileEntry != NULL)
38 {
39 fileEntry->base.name = name;
40 fileEntry->base.length = length;
41 fileEntry->base.whenModified = whenModified;
43 }
44
45 return (FileDirEntry*)fileEntry;
46}
47
55DirEntry* _NewDirEntry(const char* name, time_t whenModified)
56{
57 DirEntry* dirEntry = calloc(1, sizeof(DirEntry));
58 if (dirEntry != NULL)
59 {
60 dirEntry->base.name = name;
61 dirEntry->base.whenModified = whenModified;
63 }
64
65 return dirEntry;
66}
67
73void _AddChild(DirEntry* dirEntry, FileDirEntry* child)
74{
75 if (dirEntry != NULL && child != NULL)
76 {
77 if (dirEntry->_children == NULL)
78 {
79 dirEntry->_children = child;
80 }
81 else
82 {
83 FileDirEntry* nextChild = dirEntry->_children;
84 while (nextChild->next != NULL)
85 {
86 nextChild = nextChild->next;
87 }
88 nextChild->next = child;
89 }
90 }
91}
92
100static void _DestroyTree(FileDirEntry* root)
101{
102 if (root != NULL)
103 {
105 {
106 DirEntry* dirEntry = (DirEntry*)root;
107 _DestroyTree(dirEntry->_children);
108 }
109 free(root);
110 }
111}
112
119static void _ConstructTree(void)
120{
121 if (_root == NULL)
122 {
123 time_t now = datetime_now();
124 DirEntry* dirEntry = _NewDirEntry("root", now);
125 if (dirEntry == NULL)
126 {
127 return;
128 }
129 _root = (FileDirEntry*)dirEntry;
130
131 FileDirEntry* fileEntry = _NewFileEntry("FileA.txt", 101, now);
132 if (fileEntry == NULL)
133 {
135 return;
136 }
137 _AddChild(dirEntry, fileEntry);
138
139 fileEntry = _NewFileEntry("FileB.txt", 102, now);
140 if (fileEntry == NULL)
141 {
143 return;
144 }
145 _AddChild(dirEntry, fileEntry);
146
147 fileEntry = _NewFileEntry("FileC.txt", 103, now);
148 if (fileEntry == NULL)
149 {
151 return;
152 }
153 _AddChild(dirEntry, fileEntry);
154
155 DirEntry* subDirEntry = _NewDirEntry("subdir1", now);
156 if (subDirEntry == NULL)
157 {
159 return;
160 }
161 _AddChild(dirEntry, (FileDirEntry*)subDirEntry);
162
163 fileEntry = _NewFileEntry("FileD.txt", 104, now);
164 if (fileEntry == NULL)
165 {
167 return;
168 }
169 _AddChild(subDirEntry, fileEntry);
170
171 fileEntry = _NewFileEntry("FileE.txt", 105, now);
172 if (fileEntry == NULL)
173 {
175 return;
176 }
177 _AddChild(subDirEntry, fileEntry);
178
179 DirEntry* subsubDirEntry = _NewDirEntry("subdir2", now);
180 if (subsubDirEntry == NULL)
181 {
183 return;
184 }
185 _AddChild(subDirEntry, (FileDirEntry*)subsubDirEntry);
186
187
188 fileEntry = _NewFileEntry("FileF.txt", 106, now);
189 if (fileEntry == NULL)
190 {
192 return;
193 }
194 _AddChild(subsubDirEntry, fileEntry);
195
196 fileEntry = _NewFileEntry("FileG.txt", 107, now);
197 if (fileEntry == NULL)
198 {
200 return;
201 }
202 _AddChild(subsubDirEntry, fileEntry);
203 }
204}
205
206
207//-----------------------------------------------------------------------------
208//-----------------------------------------------------------------------------
209
219static FileDirEntry* _FindEntry(char* path)
220{
222
223 FileDirEntry* root = _root;
224 if (root != NULL)
225 {
226 SplitList pathComponents = { 0 };
227 split(path, "/", &pathComponents);
228 if (pathComponents.strings_count > 0)
229 {
230 size_t numComponents = pathComponents.strings_count;
231 for (size_t index = 0; index < numComponents; index++)
232 {
233 const char* rootName = FileDirEntry_GetName(root);
234 if (strcmp(rootName, pathComponents.strings[index]) != 0)
235 {
236 // Mismatch in path to this entry, bad path
237 root = NULL;
238 break;
239 }
240
241 if ((index + 1) >= numComponents)
242 {
243 // Reached end of path so we found what was asked for.
244 break;
245 }
246
247 // Still haven't reached end of specified path, look at
248 // the current root for children.
249
251 if (child == NULL)
252 {
253 // Path included leaf in the middle, bad path
254 break;
255 }
256
257 root = NULL; // Assume we won't find anything
258 // Look ahead in the path for a matching child.
259 const char* childComponent = pathComponents.strings[index + 1];
260 while (child != NULL)
261 {
262 const char* childName = FileDirEntry_GetName(child);
263 if (strcmp(childName, childComponent) == 0)
264 {
265 root = child;
266 break;
267 }
268 child = child->next;
269 }
270
271 if (root == NULL)
272 {
273 // Couldn't find matching child, bad path
274 break;
275 }
276 }
277 }
278 SplitList_Clear(&pathComponents);
279 }
280
281 return root;
282}
283
285// Composite_FileAccess_GetEntry()
288{
289 FileDirEntry* foundEntry = NULL;
290
291 if (path != NULL)
292 {
293 // _FindEntry() modifies the path string so create duplicate to work with.
294 char* filepath = STRDUP(path);
295 if (filepath != NULL)
296 {
297 replace_chr(filepath, '\\', '/');
298 foundEntry = _FindEntry(filepath);
299
300 free(filepath);
301 }
302 }
303
304 return foundEntry;
305}
FileDirEntry * Composite_FileAccess_GetEntry(const char *path)
Return a FileDirEntry object representing the specified file "path" from an internal list of data ent...
DirEntry * _NewDirEntry(const char *name, time_t whenModified)
Create a new DirEntry object with the specified properties.
FileDirEntry * _NewFileEntry(const char *name, long length, time_t whenModified)
Create a new FileEntry object with the specified properties.
static void _ConstructTree(void)
Construct a file/directory tree with a predefined set of files and directories. If there are any out ...
void _AddChild(DirEntry *dirEntry, FileDirEntry *child)
Add a child to the given DirEntry object.
static FileDirEntry * _root
The root of the constructed file/directory tree.
static FileDirEntry * _FindEntry(char *path)
Search the file/directory "tree" for an entry that matches the given file "path". The file path is a ...
static void _DestroyTree(FileDirEntry *root)
Destroy a file/directory tree given its root. Each element of the hierarchical tree needs to be freed...
const char * FileDirEntry_GetName(FileDirEntry *entry)
Retrieve the name of the given FileDirEntry object.
FileDirEntry * FileDirEntry_GetChildren(FileDirEntry *entry)
Retrieve a pointer to the first child of the given FileDirEntry object. If the entry does not support...
@ FileDirType_Directory
Represents a directory entry that can contain other FileDirEntry components.
@ FileDirType_File
Represents a file entry.
Declaration of the Composite_FileAccess class used in the Composite Pattern.
Implementation of the FileDirEntry, FileEntry, and DirEntry classes used in the Composite Pattern.
time_t datetime_now(void)
Retrieve a time stamp that represents the current time.
Definition: datetime.c:27
void replace_chr(char *s, char c1, char c2)
Replace all occurrences of narrow character c1 with narrow character c2 in s, using case-sensitive se...
Definition: replace.c:17
void SplitList_Clear(SplitList *list)
Clear the given SplitList object so it can be reused again. Releases the list of sub-strings (but doe...
Definition: split.c:30
void split(char *s, const char *splitChars, SplitList *components)
Split the given path into multiple strings based on the given delimiter. The pointers to each string ...
Definition: split.c:72
Declaration of the STRDUP macro that hides the differences between how strdup() is declared in differ...
#define STRDUP
Define STRDUP to be the operating system-specific version of strdup().
Definition: strdup.h:17
Represents a Directory entry.
FileDirEntry * _children
Linked list of possible children. NULL if empty.
FileDirEntry base
Base FileDirEntry identifying this entry.
Structure representing a File (FileEntry) or Directory (DirEntry) entry. This is included as the firs...
FileDirTypes fileDirType
Value from the FileDirTypes enumeration indicating what type of entry this is a part of....
const char * name
Name of this entry. Use the FileDirEntry_GetName() function to get this value.
struct FileDirEntry * next
Points to the next entry in a linked list of FileDirEntry objects. NULL means no more in list.
long length
Length of this entry. For DirEntry objects, this is computed when getting the length by calling the F...
time_t whenModified
Timestamp of when this entry was last "modified". Use the FileDirEntry_GetWhenModified() function to ...
Represents a File entry.
FileDirEntry base
Base FileDirEntry identifying this entry.
Represents a collection of sub-strings split from a single string using the split() function.
Definition: helpers/split.h:17
size_t strings_count
Number of sub-strings.
Definition: helpers/split.h:19
const char ** strings
Pointers to each sub-string.
Definition: helpers/split.h:18