]> git.tdb.fi Git - libs/datafile.git/blob - source/packsource.cpp
Cosmetic changes
[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(nullptr, 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 nullptr;
159 }
160
161
162 PackSource::Pack::Pack(IO::Seekable *i, const string &fn):
163         filename(fn),
164         io(i)
165 { }
166
167 PackSource::Pack::Pack(const Pack &other):
168         filename(other.filename),
169         io(other.io),
170         base_offset(other.base_offset)
171 {
172         for(const File &f: other.files)
173                 files.push_back(File(f, *this));
174 }
175
176 void PackSource::Pack::collect_files(FileMap &fm, const string &filter) const
177 {
178         if(filter.empty())
179         {
180                 for(const File &f: files)
181                         fm[f.get_filename()] = &f;
182         }
183         else
184         {
185                 Regex re(filter);
186                 for(const File &f: files)
187                         if(re.match(f.get_filename()))
188                                 fm[f.get_filename()] = &f;
189         }
190 }
191
192 void PackSource::Pack::translate_files(FileMap &fm, const Pack &source) const
193 {
194         for(auto i=files.begin(), j=source.files.begin(); (i!=files.end() && j!=source.files.end()); ++i, ++j)
195         {
196                 auto k = fm.find(i->get_filename());
197                 if(k!=fm.end() && k->second==&*j)
198                         k->second = &*i;
199         }
200 }
201
202
203 PackSource::File::File(const Pack &p, const string &fn):
204         pack(p),
205         filename(fn)
206 { }
207
208 PackSource::File::File(const File &other, const Pack &p):
209         pack(p),
210         filename(other.filename),
211         offset(other.offset),
212         length(other.length),
213         collection(other.collection)
214 {
215         for(const Object &o: other.objects)
216                 objects.push_back(Object(o, *this));
217 }
218
219 RefPtr<IO::Seekable> PackSource::File::open() const
220 {
221         if(pack.get_io())
222                 // TODO Performance may be poor without buffering
223                 return new IO::Slice(*pack.get_io(), pack.get_base_offset()+offset, length);
224         else
225         {
226                 IO::BufferedFile *io_file = new IO::BufferedFile(pack.get_filename());
227                 IO::Slice *io_slice = new IO::Slice(*io_file, pack.get_base_offset()+offset, length);
228                 io_slice->signal_deleted.connect(sigc::bind(sigc::ptr_fun(delete_io), io_file));
229                 return io_slice;
230         }
231 }
232
233 PackSource::FileInfo PackSource::File::get_info() const
234 {
235         FileInfo info;
236         info.name = filename;
237         info.size = length;
238         return info;
239 }
240
241 string PackSource::File::get_full_name() const
242 {
243         return format("%s/%s", pack.get_filename(), filename);
244 }
245
246 void PackSource::File::collect_objects(ObjectMap &objs) const
247 {
248         for(const Object &o: objects)
249                 objs[o.get_name()] = &o;
250 }
251
252 void PackSource::File::translate_objects(ObjectMap &objs, const File &source) const
253 {
254         for(auto i=objects.begin(), j=source.objects.begin(); (i!=objects.end() && j!=source.objects.end()); ++i, ++j)
255         {
256                 auto k = objs.find(i->get_name());
257                 if(k!=objs.end() && k->second==&*j)
258                         k->second = &*i;
259         }
260 }
261
262
263 PackSource::Object::Object(const File &f, const string &n, const string &k):
264         file(f),
265         name(n),
266         keyword(k)
267 { }
268
269 PackSource::Object::Object(const Object &other, const File &f):
270         file(f),
271         name(other.name),
272         keyword(other.keyword)
273 { }
274
275
276 PackSource::Pack::Loader::Loader(Pack &p):
277         ObjectLoader<Pack>(p)
278 {
279         add("file",        &Loader::file);
280         add("base_offset", &Pack::base_offset);
281 }
282
283 void PackSource::Pack::Loader::file(const string &fn)
284 {
285         obj.files.push_back(File(obj, fn));
286         load_sub(obj.files.back());
287 }
288
289
290 PackSource::File::Loader::Loader(File &f):
291         ObjectLoader<File>(f)
292 {
293         add("object", &Loader::object);
294         add("slice",  &File::offset, &File::length);
295 }
296
297 void PackSource::File::Loader::finish()
298 {
299         if(!obj.collection)
300         {
301                 PackSource::Object ob(obj, obj.filename, string());
302                 obj.objects.push_back(ob);
303         }
304 }
305
306 void PackSource::File::Loader::object(const string &name, const string &kwd)
307 {
308         obj.objects.push_back(PackSource::Object(obj, name, kwd));
309         obj.collection = true;
310 }
311
312 } // namespace DataFile
313 } // namespace Msp