]> git.tdb.fi Git - libs/datafile.git/blob - source/loader.cpp
Add a way to combine multiple loaders
[libs/datafile.git] / source / loader.cpp
1 #include <msp/strings/format.h>
2 #include "dataerror.h"
3 #include "loader.h"
4 #include "type.h"
5
6 using namespace std;
7
8 namespace {
9
10 template<typename T>
11 struct Set
12 {
13         T &ref;
14         T orig;
15
16         Set(T &r, const T &v): ref(r), orig(r) { r = v; }
17         ~Set() { ref = orig; }
18 };
19
20 bool signature_match(char s, char a)
21 {
22         if(s==a)
23                 return true;
24         if(s==Msp::DataFile::IntType::signature && a==Msp::DataFile::FloatType::signature)
25                 return true;
26         return false;
27 }
28
29 bool signature_match(const string &st_sig, const string &act_sig)
30 {
31         if(act_sig=="*")
32                 return true;
33         else if(act_sig.size()==2 && act_sig[1]=='*')
34         {
35                 for(string::const_iterator i=st_sig.begin(); i!=st_sig.end(); ++i)
36                         if(*i!=act_sig[0])
37                                 return false;
38
39                 return true;
40         }
41         else if(st_sig.size()==act_sig.size())
42         {
43                 for(unsigned i=0; i<st_sig.size(); ++i)
44                         if(!signature_match(st_sig[i], act_sig[i]))
45                                 return false;
46
47                 return true;
48         }
49         else
50                 return false;
51 }
52
53 }
54
55
56 namespace Msp {
57 namespace DataFile {
58
59 class unknown_keyword: public runtime_error
60 {
61 public:
62         unknown_keyword(const std::string &k):
63                 runtime_error(k)
64         { }
65
66         virtual ~unknown_keyword() throw() { }
67 };
68
69
70 class invalid_signature: public runtime_error
71 {
72 public:
73         invalid_signature(const std::string &k, const std::string &s):
74                 runtime_error(format("%s %s", k, s))
75         { }
76
77         virtual ~invalid_signature() throw() { }
78 };
79
80
81 Loader::Loader():
82         cur_st(0),
83         check_sub_loads(false)
84 { }
85
86 Loader::~Loader()
87 {
88         for(ActionMap::iterator i=actions.begin(); i!=actions.end(); ++i)
89                 delete i->second;
90 }
91
92 void Loader::load(Parser &p)
93 {
94         while(p)
95         {
96                 Statement st = p.parse();
97                 if(st.valid)
98                         load_statement(st);
99         }
100         finish();
101 }
102
103 void Loader::load(const Statement &st)
104 {
105         for(list<Statement>::const_iterator i=st.sub.begin(); i!=st.sub.end(); ++i)
106                 load_statement(*i);
107         finish();
108 }
109
110 void Loader::load_statement(const Statement &st)
111 {
112         Set<const Statement *> set_cst(cur_st, &st);
113
114         try
115         {
116                 if(!aux_loaders.empty() && !has_keyword(st.keyword))
117                 {
118                         for(list<Loader *>::const_iterator i=aux_loaders.begin(); i!=aux_loaders.end(); ++i)
119                                 if((*i)->has_keyword(st.keyword))
120                                         return (*i)->load_statement(st);
121                 }
122
123                 LoaderAction *act = find_action(ActionKey(st.keyword, st.get_signature()));
124                 if(act)
125                 {
126                         sub_loaded = false;
127                         act->execute(*this, st);
128                         if(check_sub_loads && !st.sub.empty() && !sub_loaded)
129                                 throw logic_error("substatements ignored");
130                 }
131         }
132         catch(const data_error &)
133         {
134                 throw;
135         }
136         catch(const exception &e)
137         {
138                 throw data_error(st.source, st.line, e);
139         }
140 }
141
142 void Loader::load_sub_with(Loader &ldr)
143 {
144         if(!cur_st)
145                 throw logic_error("no current statement");
146
147         ldr.load(*cur_st);
148         sub_loaded = true;
149 }
150
151 void Loader::add(const string &kwd, LoaderAction *act)
152 {
153         ActionKey key(kwd, (act ? act->get_signature() : "*"));
154         ActionMap::iterator i = actions.find(key);
155         if(i!=actions.end())
156         {
157                 delete i->second;
158                 i->second = act;
159         }
160         else
161                 actions[key] = act;
162 }
163
164 void Loader::add_auxiliary_loader(Loader &ldr)
165 {
166         aux_loaders.push_back(&ldr);
167 }
168
169 bool Loader::has_keyword(const string &kwd) const
170 {
171         ActionMap::const_iterator i = actions.lower_bound(ActionKey(kwd, string()));
172         return i!=actions.end() && i->first.keyword==kwd;
173 }
174
175 LoaderAction *Loader::find_action(const ActionKey &key) const
176 {
177         ActionMap::const_iterator begin = actions.lower_bound(ActionKey(key.keyword, string()));
178         ActionMap::const_iterator end = actions.upper_bound(ActionKey(key.keyword, "~"));
179
180         if(begin==end)
181                 throw unknown_keyword(key.keyword);
182
183         for(ActionMap::const_iterator i=begin; i!=end; ++i)
184                 if(signature_match(key.signature, i->first.signature))
185                         return i->second;
186
187         throw invalid_signature(key.keyword, key.signature);
188 }
189
190 const string &Loader::get_source() const
191 {
192         if(!cur_st)
193                 throw logic_error("no current statement");
194         return cur_st->source;
195 }
196
197
198 Loader::ActionKey::ActionKey(const string &k, const string &s):
199         keyword(k),
200         signature(s)
201 { }
202
203 bool Loader::ActionKey::operator<(const ActionKey &other) const
204 {
205         if(keyword!=other.keyword)
206                 return keyword<other.keyword;
207         return signature<other.signature;
208 }
209
210 } // namespace DataFile
211 } // namespace Msp