1 : /*
2 : * Serialize a tagged collection to a text file
3 : *
4 : * Copyright (C) 2003--2008 Enrico Zini <enrico@debian.org>
5 : *
6 : * This library is free software; you can redistribute it and/or
7 : * modify it under the terms of the GNU Lesser General Public
8 : * License as published by the Free Software Foundation; either
9 : * version 2.1 of the License, or (at your option) any later version.
10 : *
11 : * This library is distributed in the hope that it will be useful,
12 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 : * Lesser General Public License for more details.
15 : *
16 : * You should have received a copy of the GNU Lesser General Public
17 : * License along with this library; if not, write to the Free Software
18 : * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 : */
20 :
21 : #ifndef TAGCOLL_TEXTFORMAT_TCC
22 : #define TAGCOLL_TEXTFORMAT_TCC
23 :
24 : #include <tagcoll/TextFormat.h>
25 : #include <tagcoll/patch.h>
26 :
27 : #include <wibble/exception.h>
28 : #include <wibble/empty.h>
29 : #include <wibble/operators.h>
30 :
31 : #include <ostream>
32 :
33 : using namespace std;
34 : using namespace wibble;
35 : using namespace wibble::operators;
36 :
37 0 : static void printTagset(const std::set<string>& ts, FILE* out)
38 : {
39 0 : for (std::set<string>::const_iterator i = ts.begin();
40 : i != ts.end(); i++)
41 0 : if (i == ts.begin())
42 : {
43 0 : if (fprintf(out, "%s", i->c_str()) < 0)
44 0 : throw wibble::exception::System("writing tagset");
45 : }
46 : else
47 : {
48 0 : if (fprintf(out, ", %s", i->c_str()) < 0)
49 0 : throw wibble::exception::System("writing tagset");
50 : }
51 0 : }
52 :
53 : namespace tagcoll {
54 : namespace textformat {
55 :
56 126871 : inline static void outString(const std::string& str, FILE* out, const char* what)
57 : {
58 126871 : if (fwrite(str.data(), str.size(), 1, out) != 1)
59 0 : throw wibble::exception::System(string("writing ") + what);
60 126871 : }
61 :
62 : template<typename Items, typename Tags>
63 21145 : StdioWriter& StdioWriter::operator=(const std::pair<Items, Tags>& data)
64 : {
65 42290 : for (typename Items::const_iterator i = data.first.begin();
66 : i != data.first.end(); ++i)
67 : {
68 21145 : if (i != data.first.begin())
69 0 : if (fputs(", ", out) == EOF)
70 0 : throw wibble::exception::System("writing comma after item");
71 21145 : outString(*i, out, "item");
72 : }
73 21145 : if (data.second.begin() != data.second.end())
74 : {
75 21145 : if (fputs(": ", out) == EOF)
76 0 : throw wibble::exception::System("writing colon after items");
77 126871 : for (typename Tags::const_iterator i = data.second.begin();
78 : i != data.second.end(); ++i)
79 : {
80 105726 : if (i != data.second.begin())
81 84581 : if (fputs(", ", out) == EOF)
82 0 : throw wibble::exception::System("writing comma after tag");
83 105726 : outString(*i, out, "tag");
84 : }
85 : }
86 21145 : if (fputc('\n', out) == EOF)
87 0 : throw wibble::exception::System("writing newline after tagset");
88 21145 : return *this;
89 : }
90 :
91 : template<typename Items, typename Tags>
92 : OstreamWriter& OstreamWriter::operator=(const std::pair<Items, Tags>& data)
93 : {
94 : for (typename Items::const_iterator i = data.first.begin();
95 : i != data.first.end(); ++i)
96 : {
97 : if (i != data.first.begin())
98 : out << ", ";
99 : out << *i;
100 : }
101 : if (data.second.begin() != data.second.end())
102 : {
103 : out << ": ";
104 : for (typename Tags::const_iterator i = data.second.begin();
105 : i != data.second.end(); ++i)
106 : {
107 : if (i != data.second.begin())
108 : out << ", ";
109 : out << *i;
110 : }
111 : }
112 : out << endl;
113 : return *this;
114 : }
115 :
116 :
117 :
118 : // item1, item2, item3: tag1, tag2, tag3
119 :
120 : //#define TRACE_PARSE
121 : template<typename OUT>
122 2 : void parse(input::Input& in, OUT out)
123 : {
124 2 : string item;
125 :
126 2 : std::set<string> itemset;
127 2 : std::set<string> tagset;
128 : int sep;
129 2 : enum {ITEMS, TAGS} state = ITEMS;
130 2 : int line = 1;
131 253744 : do
132 : {
133 : try {
134 253744 : sep = parseElement(in, item);
135 0 : } catch (tagcoll::exception::Parser& e) {
136 : // Add the line number and propagate
137 0 : e.line(line);
138 0 : throw e;
139 : }
140 :
141 507488 : if (item.size() != 0)
142 : {
143 253742 : if (state == ITEMS)
144 42290 : itemset |= item;
145 : else
146 211452 : tagset |= item;
147 : }
148 :
149 253744 : switch (sep)
150 : {
151 : case '\n':
152 42290 : line++;
153 : case input::Input::Eof:
154 42292 : if (!(itemset.empty() && tagset.empty()))
155 : {
156 42290 : if (itemset.empty())
157 0 : throw tagcoll::exception::Input(line, "no elements before `:' separator");
158 42290 : if (tagset.empty())
159 0 : *out = make_pair(itemset, wibble::Empty<std::string>());
160 : else
161 42290 : *out = make_pair(itemset, tagset);
162 42290 : ++out;
163 : }
164 42292 : itemset.clear();
165 42292 : tagset.clear();
166 42292 : state = ITEMS;
167 42292 : break;
168 : case ':':
169 42290 : if (state == TAGS)
170 0 : throw tagcoll::exception::Input(line, "separator `:' appears twice");
171 42290 : state = TAGS;
172 : break;
173 : default:
174 2 : break;
175 : }
176 : } while (sep != input::Input::Eof);
177 2 : }
178 :
179 : template<typename OUT> template<typename ITEMS, typename TAGS>
180 0 : PatchAssembler<OUT>& PatchAssembler<OUT>::operator=(const std::pair<ITEMS, TAGS>& data)
181 : {
182 0 : std::set<std::string> added;
183 0 : std::set<std::string> removed;
184 :
185 0 : for (typename TAGS::const_iterator i = data.second.begin();
186 : i != data.second.end(); ++i)
187 : {
188 0 : std::string tag = i->substr(1);
189 0 : if (!tag.empty())
190 : {
191 0 : if ((*i)[0] == '-')
192 0 : removed.insert(tag);
193 0 : else if ((*i)[0] == '+')
194 0 : added.insert(tag);
195 : }
196 : }
197 :
198 0 : for (typename ITEMS::const_iterator i = data.first.begin();
199 : i != data.first.end(); ++i)
200 : {
201 0 : std::string it = *i;
202 0 : if (!it.empty())
203 : {
204 0 : *out = Patch<std::string, std::string>(it, added, removed);
205 0 : ++out;
206 : }
207 : }
208 0 : return *this;
209 : }
210 :
211 :
212 :
213 : template<typename ITEM, typename TAG, typename ITEMSER, typename TAGSER>
214 : void outputPatch(
215 : ITEMSER& itemconv,
216 : TAGSER& tagconv,
217 : const PatchList<ITEM, TAG>& patch,
218 : FILE* out)
219 : {
220 : for (typename PatchList<ITEM, TAG>::const_iterator i = patch.begin();
221 : i != patch.end(); i++)
222 : {
223 : string sitem = itemconv(i->first);
224 : if (fprintf(out, "%s: ", sitem.c_str()) < 0)
225 : throw wibble::exception::System("writing item");
226 :
227 : std::set<string> stags;
228 : for (typename std::set<TAG>::const_iterator j = i->second.added.begin();
229 : j != i->second.added.end(); j++)
230 : stags |= "+"+tagconv(*j);
231 : for (typename std::set<TAG>::const_iterator j = i->second.removed.begin();
232 : j != i->second.removed.end(); j++)
233 : stags |= "-"+tagconv(*j);
234 :
235 : printTagset(stags, out);
236 : if (fprintf(out, "\n") < 0)
237 : throw wibble::exception::System("writing newline after tagset");
238 : }
239 : }
240 :
241 : template<typename ITEM, typename TAG, typename ITEMSER, typename TAGSER>
242 : template<typename ITEMS, typename TAGS>
243 : PatchBuilder<ITEM, TAG, ITEMSER, TAGSER>& PatchBuilder<ITEM, TAG, ITEMSER, TAGSER>::operator=(const std::pair<ITEMS, TAGS>& data)
244 : {
245 : std::set<TAG> added;
246 : std::set<TAG> removed;
247 :
248 : for (typename TAGS::const_iterator i = data.second.begin();
249 : i != data.second.end(); ++i)
250 : {
251 : TAG tag = tagconv(i->substr(1));
252 : if (tag != TAG())
253 : {
254 : if ((*i)[0] == '-')
255 : removed.insert(tag);
256 : else if ((*i)[0] == '+')
257 : added.insert(tag);
258 : }
259 : }
260 :
261 : for (typename ITEMS::const_iterator i = data.first.begin();
262 : i != data.first.end(); ++i)
263 : {
264 : ITEM it = itemconv(*i);
265 : if (it != ITEM())
266 : patch.addPatch(Patch<ITEM, TAG>(it, added, removed));
267 : }
268 : return *this;
269 : }
270 :
271 :
272 : template<typename ITEM, typename TAG, typename ITEMSER, typename TAGSER>
273 : PatchList<ITEM, TAG> parsePatch(
274 : ITEMSER& itemconv,
275 : TAGSER& tagconv,
276 : input::Input& in)
277 : {
278 : PatchList<ITEM, TAG> patch;
279 : parse(in, patchBuilder(patch, itemconv, tagconv));
280 : return patch;
281 : }
282 :
283 : }
284 6 : }
285 :
286 : #include <tagcoll/patch.tcc>
287 :
288 : #endif
289 :
290 : // vim:set ts=4 sw=4:
|