Line data Source code
1 : /*
2 : Zipios -- a small C++ library that provides easy access to .zip files.
3 :
4 : Copyright (C) 2000-2007 Thomas Sondergaard
5 : Copyright (C) 2015-2019 Made to Order Software Corporation
6 :
7 : This library is free software; you can redistribute it and/or
8 : modify it under the terms of the GNU Lesser General Public
9 : License as published by the Free Software Foundation; either
10 : version 2.1 of the License, or (at your option) any later version.
11 :
12 : This library is distributed in the hope that it will be useful,
13 : but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : Lesser General Public License for more details.
16 :
17 : You should have received a copy of the GNU Lesser General Public
18 : License along with this library; if not, write to the Free Software
19 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 : */
21 :
22 : /** \file
23 : * \brief Implementation of zipios::CollectionCollection.
24 : *
25 : * This class derives from zipios::FileCollection. It allows to many
26 : * any number of collections within one collection.
27 : */
28 :
29 : #include "zipios/collectioncollection.hpp"
30 :
31 : #include "zipios/zipiosexceptions.hpp"
32 :
33 : #include "zipios_common.hpp"
34 :
35 :
36 : namespace zipios
37 : {
38 :
39 :
40 : namespace
41 : {
42 :
43 : /** \brief Search for an entry.
44 : *
45 : * This function searchs for an entry that match the given name.
46 : * If that entry exists, the \p it parameter will be pointing
47 : * to it.
48 : *
49 : * The \p cep parameter is also set to the object found.
50 : *
51 : * \param[in] collections The collections to search for the specified name.
52 : * \param[in] name The name of the entry to search.
53 : * \param[out] cep The pointer to the entry found.
54 : * \param[out] file_collection A reference to a smarter pointer where we
55 : * can save the found file collection.
56 : * \param[in] matchpath How the name of the entry is compared with \p name.
57 : */
58 983 : void matchEntry(CollectionCollection::vector_t collections, std::string const& name, FileEntry::pointer_t& cep, FileCollection::pointer_t& file_collection, CollectionCollection::MatchPath matchpath)
59 : {
60 1992 : for(auto it = collections.begin(); it != collections.end(); ++it)
61 : {
62 1723 : cep = (*it)->getEntry(name, matchpath);
63 1723 : if(cep)
64 : {
65 714 : file_collection = *it;
66 714 : return;
67 : }
68 : }
69 269 : cep.reset();
70 269 : file_collection.reset();
71 : }
72 :
73 : } // no name namespace
74 :
75 :
76 : /** \class CollectionCollection
77 : * \brief A collection of collections.
78 : *
79 : * CollectionCollection is a FileCollection that consists of an
80 : * arbitrary number of FileCollection's. With a CollectionCollection
81 : * the user can use multiple FileCollection objects transparently, making
82 : * it easy for a program to keep some of its files in a zip archive and
83 : * others stored in ordinary files. CollectionCollection can be used
84 : * to create a simple virtual filesystem, where all collections are
85 : * mounted on /. If more than one collection contain a file with
86 : * the same path only the one in the first added collection is
87 : * accessible.
88 : */
89 :
90 :
91 :
92 : /** \brief Initialize a CollectionCollection object.
93 : *
94 : * The constructor initializes the CollectionCollection as a valid
95 : * collection.
96 : */
97 19 : CollectionCollection::CollectionCollection()
98 : {
99 19 : m_valid = true; // we are valid even though we are empty!
100 19 : }
101 :
102 :
103 : /** \brief Copy a CollectionCollection in another.
104 : *
105 : * This function copies a collection of collections in another. Note
106 : * that all the children get cloned so the copy can be edited without
107 : * modify the source and vice versa.
108 : *
109 : * \param[in] src The source to copy in the new CollectionCollection.
110 : */
111 22 : CollectionCollection::CollectionCollection(CollectionCollection const& src)
112 22 : : FileCollection(src)
113 : {
114 22 : m_collections.reserve(src.m_collections.size());
115 51 : for(auto it = src.m_collections.begin(); it != src.m_collections.end(); ++it)
116 : {
117 29 : m_collections.push_back((*it)->clone());
118 : }
119 22 : }
120 :
121 :
122 : /** \brief Copy assignment operator.
123 : *
124 : * This assignment operator copies \p rhs to this collection replacing
125 : * the file entries that exist in this collection.
126 : *
127 : * Note that the source file entries are cloned in the destination so
128 : * modifying this collection will not modify the source.
129 : *
130 : * \param[in] rhs The source to copy in this collection.
131 : */
132 7 : CollectionCollection& CollectionCollection::operator = (CollectionCollection const& rhs)
133 : {
134 7 : FileCollection::operator = (rhs);
135 :
136 7 : if(this != &rhs)
137 : {
138 7 : m_collections.clear();
139 : // A call to the CollectionCollection::size() function has side
140 : // effects, try to avoid them at this time
141 : //m_collections.reserve(rhs.m_collections.size());
142 11 : for(auto it = rhs.m_collections.begin(); it != rhs.m_collections.end(); ++it)
143 : {
144 4 : m_collections.push_back((*it)->clone());
145 : }
146 : }
147 :
148 7 : return *this;
149 : }
150 :
151 :
152 : /** \brief Create a clone of this object.
153 : *
154 : * This function creates a heap allocated clone of the CollectionCollection.
155 : *
156 : * Note that all the collections that this CollectionCollection points
157 : * to are all going to get cloned.
158 : *
159 : * \return A shared pointer to a copy of this CollectionCollection.
160 : */
161 15 : FileCollection::pointer_t CollectionCollection::clone() const
162 : {
163 15 : return FileCollection::pointer_t(new CollectionCollection(*this));
164 : }
165 :
166 :
167 : /** \brief Clean up this CollectionCollection object.
168 : *
169 : * This function ensures that the CollectionCollection object
170 : * is cleaned up before deallcoating the memory.
171 : */
172 97 : CollectionCollection::~CollectionCollection()
173 : {
174 41 : close();
175 56 : }
176 :
177 :
178 : /** \brief Add a FileCollection to this CollectionCollection.
179 : *
180 : * This function adds a collection in this CollectionCollection.
181 : * Since a CollectionCollection is itself a FileCollection, you
182 : * may add a CollectionCollection to another CollectionCollection.
183 : *
184 : * \note
185 : * The FileCollection to be added must be valid or it will be ignored.
186 : *
187 : * \param[in] collection The collection to add.
188 : *
189 : * \return true if the collection was added successfully.
190 : *
191 : * \sa addCollection(FileCollection::pointer_t collection);
192 : */
193 42 : bool CollectionCollection::addCollection(FileCollection const& collection)
194 : {
195 42 : mustBeValid();
196 :
197 : /** \TODO
198 : * At this time the function verifies that you are not trying to add
199 : * a CollectionCollection to itself. However, this test is currently
200 : * really weak. We need to check whether any collection in the
201 : * input \p collection represents this collection.
202 : */
203 27 : if(this == &collection || !collection.isValid())
204 : {
205 4 : return false;
206 : }
207 :
208 23 : m_collections.push_back(collection.clone());
209 :
210 23 : return true;
211 : }
212 :
213 :
214 : /** \brief Add a collection to this CollectionCollection.
215 : *
216 : * This function adds the collection pointed to by \p collection to
217 : * this CollectionCollection.
218 : *
219 : * The CollectionCollection makes a clone of the specified \p collection
220 : * to make sure management of the child collection works as expected.
221 : *
222 : * If the collection does not get added, the function returns false.
223 : * This happens when the \p collection parameter represents an invalid
224 : * collection.
225 : *
226 : * \exception InvalidException
227 : * The function raises InvalidException if the \p collection parameter
228 : * is a null pointer.
229 : *
230 : * \param[in] collection A pointer to the collection to add.
231 : *
232 : * \return true if the collection was added successfully.
233 : *
234 : * \sa addCollection(FileCollection const& collection);
235 : */
236 17 : bool CollectionCollection::addCollection(FileCollection::pointer_t collection)
237 : {
238 17 : if(collection == nullptr)
239 : {
240 : // TBD: should we return false instead?
241 1 : throw InvalidException("CollectionCollection::addCollection(): called with a null collection pointer");
242 : }
243 :
244 16 : return addCollection(*collection);
245 : }
246 :
247 :
248 : /** \brief Close the CollectionCollection object.
249 : *
250 : * This function marks the collection as invalid in effect rendering
251 : * the collection unusable. Note that all the collections that you
252 : * previously added to this collection all get marked as invalid
253 : * (i.e. their close() function gets called.) This has the nice side
254 : * effect to release memory immediately.
255 : *
256 : * \note
257 : * This is different from creating an empty CollectionCollection
258 : * which is empty and valid.
259 : */
260 49 : void CollectionCollection::close()
261 : {
262 : // make sure to close all the children first
263 : // (although I would imagine that the m_collections.clear() should
264 : // be enough, unless someone else has a refenrence to another one
265 : // of the sub-collections--but I do not think one can get such as
266 : // reference at this point, remember that the addCollection()
267 : // creates a clone of the collection being added.)
268 105 : for(auto it = m_collections.begin(); it != m_collections.end(); ++it)
269 : {
270 : // each collection in the collection must be valid since we
271 : // may hit any one of them
272 56 : (*it)->close();
273 : }
274 49 : m_collections.clear();
275 :
276 49 : FileCollection::close();
277 49 : }
278 :
279 :
280 : /** \brief Retrieve a vector to all the collection entries.
281 : *
282 : * This function gathers the entries of all the children collections
283 : * and add them to a vector that it then returns.
284 : *
285 : * The CollectionCollection itself has no entries.
286 : *
287 : * It is possible to define a CollectionCollection as a child of
288 : * another CollectionCollection. The process repeats infinitum
289 : * as required.
290 : *
291 : * \return A copy of all the entries found in the child Collections.
292 : */
293 36 : FileEntry::vector_t CollectionCollection::entries() const
294 : {
295 36 : mustBeValid();
296 :
297 28 : FileEntry::vector_t all_entries;
298 68 : for(auto it = m_collections.begin(); it != m_collections.end(); ++it)
299 : {
300 40 : all_entries += (*it)->entries();
301 : }
302 :
303 28 : return all_entries;
304 : }
305 :
306 :
307 : /** \brief Get an entry from the collection.
308 : *
309 : * This function returns a shared pointer to a FileEntry object for
310 : * the entry with the specified name. To ignore the path part of the
311 : * filename while searching for a match, specify
312 : * FileCollection::MatchPath::IGNORE as the second argument.
313 : * (the default is FileCollection::MatchPath::MATCH.
314 : *
315 : * \warning
316 : * In case of the CollectionCollection, the matching goes from child
317 : * collection to child collection in the order they were added to
318 : * the CollectionCollection. The first match is returned and at this
319 : * point there is nothing linking a FileEntry to its collection so
320 : * you will NOT be able to retrieve an istream to access that
321 : * FileEntry data. To do that, you must directly call the
322 : * getInputStream() function. We may fix that problem at a later
323 : * time and offer the getInputStream directly on the FileEntry
324 : * instead of the collection. This is problematic at this point
325 : * since, as we can see in the zipfile.cpp, we need to have
326 : * access to the m_zs offset.
327 : *
328 : * \note
329 : * The collection must be valid or the function raises an exception.
330 : *
331 : * \param[in] name A string containing the name of the entry to get.
332 : * \param[in] matchpath Speficy MatchPath::MATCH, if the path should match
333 : * as well, specify MatchPath::IGNORE, if the path
334 : * should be ignored.
335 : *
336 : * \return A shared pointer to the found entry. The returned pointer
337 : * is null if no entry is found.
338 : *
339 : * \sa mustBeValid()
340 : */
341 704 : FileEntry::pointer_t CollectionCollection::getEntry(std::string const& name, MatchPath matchpath) const
342 : {
343 704 : mustBeValid();
344 :
345 : // Returns the first matching entry.
346 1376 : FileCollection::pointer_t file_colection;
347 688 : FileEntry::pointer_t cep;
348 :
349 688 : matchEntry(m_collections, name, cep, file_colection, matchpath);
350 :
351 1376 : return cep;
352 : }
353 :
354 :
355 : /** \brief Retrieve pointer to an istream.
356 : *
357 : * This function returns a shared pointer to an istream defined from the
358 : * named entry, which is expected to be available in this collection.
359 : *
360 : * The function returns a NULL pointer if there is no entry with the
361 : * specified name in this CollectionCollection. Note that the name is
362 : * searched in all the child collections of the CollectionCollection.
363 : *
364 : * Note that the function returns a smart pointer to an istream. In
365 : * general the CollectionCollection will not hold a copy of that pointer
366 : * meaning that if you call getInputStream() multiple times with the same
367 : * \p entry_name parameter, you get distinct istream instances each
368 : * time.
369 : *
370 : * By default the \p entry_name parameter is expected to match the full
371 : * path and filename (MatchPath::MATCH). If you are looking for a file
372 : * and want to ignore the directory name, set the matchpath parameter
373 : * to MatchPath::IGNORE.
374 : *
375 : * \param[in] entry_name The name of the file to search in the collection.
376 : * \param[in] matchpath Whether the full path or just the filename is matched.
377 : *
378 : * \return A shared pointer to an open istream for the specified entry.
379 : *
380 : * \sa FileCollection
381 : * \sa DirectoryCollection
382 : * \sa ZipFile
383 : */
384 311 : CollectionCollection::stream_pointer_t CollectionCollection::getInputStream(std::string const& entry_name, MatchPath matchpath)
385 : {
386 311 : mustBeValid();
387 :
388 590 : FileCollection::pointer_t file_collection;
389 590 : FileEntry::pointer_t cep;
390 :
391 295 : matchEntry(m_collections, entry_name, cep, file_collection, matchpath);
392 :
393 590 : return cep ? file_collection->getInputStream(entry_name) : nullptr;
394 : }
395 :
396 :
397 : /** \brief Return the size of the of this collection.
398 : *
399 : * This function computes the total size of this collection which
400 : * is to sum of the size of its child collections.
401 : *
402 : * \warning
403 : * This function has the side effect of loading all the data from
404 : * DirectoryCollection objects.
405 : *
406 : * \return The total size of the collection.
407 : */
408 63 : size_t CollectionCollection::size() const
409 : {
410 63 : mustBeValid();
411 :
412 55 : size_t sz(0);
413 158 : for(auto it = m_collections.begin(); it != m_collections.end(); ++it)
414 : {
415 103 : sz += (*it)->size();
416 : }
417 :
418 55 : return sz;
419 : }
420 :
421 :
422 : /** \brief Check whether the collection is valid.
423 : *
424 : * This function verifies that the collection is valid. If not, an
425 : * exception is raised. Many other functions from the various collection
426 : * functions are calling this function before accessing data.
427 : *
428 : * \exception InvalidStateException
429 : * This exception is raised if the m_valid field is currently false and
430 : * thus most of the collection data is considered invalid.
431 : */
432 1556 : void CollectionCollection::mustBeValid() const
433 : {
434 : // self must be valid
435 1556 : FileCollection::mustBeValid();
436 :
437 4995 : for(auto it = m_collections.begin(); it != m_collections.end(); ++it)
438 : {
439 : // each collection in the collection must be valid since we
440 : // may hit any one of them
441 3518 : (*it)->mustBeValid();
442 : }
443 1477 : }
444 :
445 :
446 3 : } // zipios namespace
447 :
448 : // Local Variables:
449 : // mode: cpp
450 : // indent-tabs-mode: nil
451 : // c-basic-offset: 4
452 : // tab-width: 4
453 : // End:
454 :
455 : // vim: ts=4 sw=4 et
|