]> git.tdb.fi Git - libs/datafile.git/blob - source/packsource.cpp
Use C++11 features to manipulate containers
[libs/datafile.git] / source / packsource.cpp
1 #include <msp/core/algorithm.h>
2 #include <msp/io/slice.h>
3 #include <msp/strings/format.h>
4 #include <msp/strings/regex.h>
5 #include "collection.h"
6 #include "packsource.h"
7
8 using namespace std;
9
10 namespace {
11
12 void delete_io(Msp::IO::Base *io)
13 {
14         delete io;
15 }
16
17 }
18
19 namespace Msp {
20 namespace DataFile {
21
22 PackSource::PackSource(const PackSource &other):
23         packs(other.packs),
24         files(other.files),
25         objects(other.objects)
26 {
27         translate_maps(other);
28 }
29
30 PackSource &PackSource::operator=(const PackSource &other)
31 {
32         packs = list<Pack>(other.packs.begin(), other.packs.end());
33         files = other.files;
34         objects = other.objects;
35         translate_maps(other);
36         return *this;
37 }
38
39 void PackSource::translate_maps(const PackSource &other)
40 {
41         for(list<Pack>::const_iterator i=packs.begin(), j=other.packs.begin(); (i!=packs.end() && j!=other.packs.end()); ++i, ++j)
42                 i->translate_files(files, *j);
43         for(FileMap::const_iterator i=files.begin(), j=other.files.begin(); (i!=files.end() && j!=other.files.end()); ++i, ++j)
44                 i->second->translate_objects(objects, *j->second);
45 }
46
47 void PackSource::add_pack_file(const string &fn)
48 {
49         add_pack_file(fn, string());
50 }
51
52 void PackSource::add_pack_file(const string &fn, const string &filter)
53 {
54         add_pack(0, fn, filter);
55 }
56
57 void PackSource::add_pack_io(IO::Seekable &io, const string &fn)
58 {
59         add_pack(&io, fn, string());
60 }
61
62 void PackSource::add_pack_io(IO::Seekable &io, const string &fn, const string &filter)
63 {
64         add_pack(&io, fn, filter);
65 }
66
67 void PackSource::add_pack(IO::Seekable *io, const string &fn, const string &filter)
68 {
69         auto i = find_if(packs, [io, &fn](const Pack &p){ return p.get_filename()==fn || (io && p.get_io()==io); });
70         if(i==packs.end())
71         {
72                 packs.push_back(Pack(io, fn));
73                 i = prev(packs.end());
74                 if(io)
75                 {
76                         DataFile::Parser parser(*io, fn);
77                         Pack::Loader loader(*i);
78                         loader.load(parser);
79                 }
80                 else
81                         DataFile::load(*i, fn);
82         }
83
84         FileMap pack_files;
85         i->collect_files(pack_files, filter);
86         for(const auto &kvp: pack_files)
87         {
88                 files[kvp.first] = kvp.second;
89                 kvp.second->collect_objects(objects);
90         }
91 }
92
93 list<PackSource::FileInfo> PackSource::list_files() const
94 {
95         list<FileInfo> result;
96         for(const auto &kvp: files)
97                 result.push_back(kvp.second->get_info());
98         return result;
99 }
100
101 bool PackSource::is_loadable(const CollectionItemTypeBase &type, const string &name) const
102 {
103         auto i = objects.find(name);
104         if(i==objects.end())
105                 return false;
106
107         // If the object has a keyword, it must match that of the type
108         if(!i->second->get_keyword().empty() && i->second->get_keyword()!=type.get_keyword())
109                 return false;
110
111         return true;
112 }
113
114 CollectionSource::NameList PackSource::get_names(const CollectionItemTypeBase &type) const
115 {
116         NameList names;
117         for(const auto &kvp: objects)
118         {
119                 if(!kvp.second->get_keyword().empty())
120                 {
121                         if(kvp.second->get_keyword()!=type.get_keyword())
122                                 continue;
123                 }
124                 else if(!type.match_name(kvp.first))
125                         continue;
126
127                 names.push_back(kvp.first);
128         }
129
130         return names;
131 }
132
133 void PackSource::load(Collection &coll, const CollectionItemTypeBase &type, const string &name) const
134 {
135         auto i = objects.find(name);
136         if(i==objects.end())
137                 return;
138
139         const File &file = i->second->get_file();
140
141         RefPtr<IO::Base> in = file.open();
142         Parser parser(*in, file.get_full_name());
143         if(file.is_collection())
144         {
145                 Collection::Loader ldr(coll);
146                 ldr.load(parser);
147         }
148         else
149                 type.load_item(coll, parser, name);
150 }
151
152 IO::Seekable *PackSource::open(const string &fn) const
153 {
154         auto i = files.find(fn);
155         if(i!=files.end())
156                 return i->second->open().release();
157
158         return 0;
159 }
160
161
162 PackSource::Pack::Pack(IO::Seekable *i, const string &fn):
163         filename(fn),
164         io(i),
165         base_offset(0)
166 { }
167
168 PackSource::Pack::Pack(const Pack &other):
169         filename(other.filename),
170         io(other.io),
171         base_offset(other.base_offset)
172 {
173         for(const File &f: other.files)
174                 files.push_back(File(f, *this));
175 }
176
177 void PackSource::Pack::collect_files(FileMap &fm, const string &filter) const
178 {
179         if(filter.empty())
180         {
181                 for(const File &f: files)
182                         fm[f.get_filename()] = &f;
183         }
184         else
185         {
186                 Regex re(filter);
187                 for(const File &f: files)
188                         if(re.match(f.get_filename()))
189                                 fm[f.get_filename()] = &f;
190         }
191 }
192
193 void PackSource::Pack::translate_files(FileMap &fm, const Pack &source) const
194 {
195         for(auto i=files.begin(), j=source.files.begin(); (i!=files.end() && j!=source.files.end()); ++i, ++j)
196         {
197                 auto k = fm.find(i->get_filename());
198                 if(k!=fm.end() && k->second==&*j)
199                         k->second = &*i;
200         }
201 }
202
203
204 PackSource::File::File(const Pack &p, const string &fn):
205         pack(p),
206         filename(fn),
207         offset(0),
208         length(0),
209         collection(false)
210 { }
211
212 PackSource::File::File(const File &other, const Pack &p):
213         pack(p),
214         filename(other.filename),
215         offset(other.offset),
216         length(other.length),
217         collection(other.collection)
218 {
219         for(const Object &o: other.objects)
220                 objects.push_back(Object(o, *this));
221 }
222
223 RefPtr<IO::Seekable> PackSource::File::open() const
224 {
225         if(pack.get_io())
226                 // TODO Performance may be poor without buffering
227                 return new IO::Slice(*pack.get_io(), pack.get_base_offset()+offset, length);
228         else
229         {
230                 IO::BufferedFile *io_file = new IO::BufferedFile(pack.get_filename());
231                 IO::Slice *io_slice = new IO::Slice(*io_file, pack.get_base_offset()+offset, length);
232                 io_slice->signal_deleted.connect(sigc::bind(sigc::ptr_fun(delete_io), io_file));
233                 return io_slice;
234         }
235 }
236
237 PackSource::FileInfo PackSource::File::get_info() const
238 {
239         FileInfo info;
240         info.name = filename;
241         info.size = length;
242         return info;
243 }
244
245 string PackSource::File::get_full_name() const
246 {
247         return format("%s/%s", pack.get_filename(), filename);
248 }
249
250 void PackSource::File::collect_objects(ObjectMap &objs) const
251 {
252         for(const Object &o: objects)
253                 objs[o.get_name()] = &o;
254 }
255
256 void PackSource::File::translate_objects(ObjectMap &objs, const File &source) const
257 {
258         for(auto i=objects.begin(), j=source.objects.begin(); (i!=objects.end() && j!=source.objects.end()); ++i, ++j)
259         {
260                 auto k = objs.find(i->get_name());
261                 if(k!=objs.end() && k->second==&*j)
262                         k->second = &*i;
263         }
264 }
265
266
267 PackSource::Object::Object(const File &f, const string &n, const string &k):
268         file(f),
269         name(n),
270         keyword(k)
271 { }
272
273 PackSource::Object::Object(const Object &other, const File &f):
274         file(f),
275         name(other.name),
276         keyword(other.keyword)
277 { }
278
279
280 PackSource::Pack::Loader::Loader(Pack &p):
281         ObjectLoader<Pack>(p)
282 {
283         add("file",        &Loader::file);
284         add("base_offset", &Pack::base_offset);
285 }
286
287 void PackSource::Pack::Loader::file(const string &fn)
288 {
289         obj.files.push_back(File(obj, fn));
290         load_sub(obj.files.back());
291 }
292
293
294 PackSource::File::Loader::Loader(File &f):
295         ObjectLoader<File>(f)
296 {
297         add("object", &Loader::object);
298         add("slice",  &File::offset, &File::length);
299 }
300
301 void PackSource::File::Loader::finish()
302 {
303         if(!obj.collection)
304         {
305                 PackSource::Object ob(obj, obj.filename, string());
306                 obj.objects.push_back(ob);
307         }
308 }
309
310 void PackSource::File::Loader::object(const string &name, const string &kwd)
311 {
312         obj.objects.push_back(PackSource::Object(obj, name, kwd));
313         obj.collection = true;
314 }
315
316 } // namespace DataFile
317 } // namespace Msp