1 #include "xmlexport.h"
2 #include "services/exportmanager.h"
3 #include "common/unused.h"
4 #include <QTextCodec>
5 
6 const QString XmlExport::docBegin = QStringLiteral("<?xml version=\"1.0\" encoding=\"%1\"?>\n");
7 
XmlExport()8 XmlExport::XmlExport()
9 {
10 }
11 
getFormatName() const12 QString XmlExport::getFormatName() const
13 {
14     return QStringLiteral("XML");
15 }
16 
standardOptionsToEnable() const17 ExportManager::StandardConfigFlags XmlExport::standardOptionsToEnable() const
18 {
19     return ExportManager::CODEC;
20 }
21 
getExportConfigFormName() const22 QString XmlExport::getExportConfigFormName() const
23 {
24     return QStringLiteral("XmlExportConfig");
25 }
26 
getConfig()27 CfgMain* XmlExport::getConfig()
28 {
29     return &cfg;
30 }
31 
validateOptions()32 void XmlExport::validateOptions()
33 {
34     bool useNs = cfg.XmlExport.UseNamespace.get();
35     EXPORT_MANAGER->updateVisibilityAndEnabled(cfg.XmlExport.Namespace, true, useNs);
36 
37     bool nsValid = !useNs || !cfg.XmlExport.Namespace.get().isEmpty();
38     EXPORT_MANAGER->handleValidationFromPlugin(nsValid, cfg.XmlExport.Namespace, tr("Enter the namespace to use (for example: http://my.namespace.org)"));
39 }
40 
defaultFileExtension() const41 QString XmlExport::defaultFileExtension() const
42 {
43     return QStringLiteral("xml");
44 }
45 
beforeExportQueryResults(const QString & query,QList<QueryExecutor::ResultColumnPtr> & columns,const QHash<ExportManager::ExportProviderFlag,QVariant> providedData)46 bool XmlExport::beforeExportQueryResults(const QString& query, QList<QueryExecutor::ResultColumnPtr>& columns, const QHash<ExportManager::ExportProviderFlag, QVariant> providedData)
47 {
48     UNUSED(providedData);
49 
50     setupConfig();
51 
52     write(docBegin.arg(codecName));
53 
54     writeln(QString("<results%2>").arg(nsStr));
55     incrIndent();
56 
57     writeln("<query>");
58     incrIndent();
59     writeln(escape(query));
60     decrIndent();
61     writeln("</query>");
62 
63     QList<DataType> columnTypes = QueryExecutor::resolveColumnTypes(db, columns, true);
64     writeln("<columns>");
65     incrIndent();
66     int i = 0;
67     DataType type;
68     for (QueryExecutor::ResultColumnPtr col : columns)
69     {
70         type = columnTypes[i];
71 
72         writeln("<column>");
73         incrIndent();
74         writeTagWithValue("displayName", col->displayName);
75         writeTagWithValue("name>", col->column);
76         writeTagWithValue("table", col->table);
77         writeTagWithValue("database", col->database);
78         writeTagWithValue("type", type.toFullTypeString());
79         decrIndent();
80         writeln("</column>");
81         i++;
82     }
83     decrIndent();
84     writeln("</columns>");
85 
86     writeln("<rows>");
87     incrIndent();
88     return true;
89 }
90 
exportQueryResultsRow(SqlResultsRowPtr row)91 bool XmlExport::exportQueryResultsRow(SqlResultsRowPtr row)
92 {
93     static const QString rowTpl = QStringLiteral("<value column=\"%1\">%2</value>");
94     static const QString nullTpl = QStringLiteral("<value column=\"%1\" null=\"true\"/>");
95 
96     writeln("<row>");
97     incrIndent();
98 
99     int i = 0;
100     for (const QVariant& value : row->valueList())
101     {
102         if (value.isNull())
103             writeln(nullTpl.arg(i));
104         else
105             writeln(rowTpl.arg(i).arg(escape(value.toString())));
106 
107         i++;
108     }
109 
110     decrIndent();
111     writeln("</row>");
112     return true;
113 }
114 
afterExportQueryResults()115 bool XmlExport::afterExportQueryResults()
116 {
117     decrIndent();
118     write("</rows>");
119     decrIndent();
120     write("</results>");
121     return true;
122 }
123 
exportTable(const QString & database,const QString & table,const QStringList & columnNames,const QString & ddl,SqliteCreateTablePtr createTable,const QHash<ExportManager::ExportProviderFlag,QVariant> providedData)124 bool XmlExport::exportTable(const QString& database, const QString& table, const QStringList& columnNames, const QString& ddl, SqliteCreateTablePtr createTable, const QHash<ExportManager::ExportProviderFlag, QVariant> providedData)
125 {
126     UNUSED(columnNames);
127     UNUSED(providedData);
128     if (isTableExport())
129     {
130         setupConfig();
131         write(docBegin.arg(codecName));
132     }
133 
134     writeln(QString("<table%1>").arg(isTableExport() ? nsStr : ""));
135     incrIndent();
136 
137     writeTagWithValue("database", database);
138     writeTagWithValue("name", table);
139     if (!createTable->withOutRowId.isNull())
140         writeln("<withoutRowId>true</withoutRowId>");
141 
142     writeTagWithValue("ddl", ddl);
143 
144     writeln("<columns>");
145     incrIndent();
146     for (SqliteCreateTable::Column* col : createTable->columns)
147     {
148         writeln("<column>");
149         incrIndent();
150         writeTagWithValue("name", col->name);
151         writeTagWithValue("type", (col->type ? col->type->toDataType().toFullTypeString() : ""));
152         if (col->constraints.size() > 0)
153         {
154             writeln("<constraints>");
155             incrIndent();
156             for (SqliteCreateTable::Column::Constraint* constr : col->constraints)
157             {
158                 writeln("<constraint>");
159                 incrIndent();
160                 writeTagWithValue("type", constr->typeString());
161                 writeTagWithValue("definition", constr->detokenize());
162                 decrIndent();
163                 writeln("</constraint>");
164             }
165             decrIndent();
166             writeln("</constraints>");
167         }
168         decrIndent();
169         writeln("</column>");
170     }
171     decrIndent();
172     writeln("</columns>");
173 
174     if (createTable->constraints.size() > 0)
175     {
176         writeln("<constraints>");
177         incrIndent();
178         for (SqliteCreateTable::Constraint* constr : createTable->constraints)
179         {
180             writeln("<constraint>");
181             incrIndent();
182             writeTagWithValue("type", constr->typeString());
183             writeTagWithValue("definition", constr->detokenize());
184             decrIndent();
185             writeln("</constraint>");
186         }
187         decrIndent();
188         writeln("</constraints>");
189     }
190 
191     writeln("<rows>");
192     incrIndent();
193     return true;
194 }
195 
exportVirtualTable(const QString & database,const QString & table,const QStringList & columnNames,const QString & ddl,SqliteCreateVirtualTablePtr createTable,const QHash<ExportManager::ExportProviderFlag,QVariant> providedData)196 bool XmlExport::exportVirtualTable(const QString& database, const QString& table, const QStringList& columnNames, const QString& ddl, SqliteCreateVirtualTablePtr createTable, const QHash<ExportManager::ExportProviderFlag, QVariant> providedData)
197 {
198     UNUSED(providedData);
199 
200     if (isTableExport())
201     {
202         setupConfig();
203         write(docBegin.arg(codecName));
204     }
205 
206     writeln(QString("<table%1>").arg(isTableExport() ? nsStr : ""));
207     incrIndent();
208 
209     writeTagWithValue("database", database);
210     writeTagWithValue("name", table);
211     writeln("<virtual>true</virtual>");
212     writeTagWithValue("module", createTable->module);
213     writeTagWithValue("ddl", ddl);
214 
215     writeln("<columns>");
216     incrIndent();
217     for (const QString& col : columnNames)
218     {
219         writeln("<column>");
220         incrIndent();
221         writeTagWithValue("name", col);
222         decrIndent();
223         writeln("</column>");
224     }
225     decrIndent();
226     writeln("</columns>");
227 
228     if (createTable->args.size() > 0)
229     {
230         writeln("<moduleArgs>");
231         incrIndent();
232         for (const QString& arg : createTable->args)
233             writeTagWithValue("arg", arg);
234 
235         decrIndent();
236         writeln("</moduleArgs>");
237     }
238 
239     writeln("<rows>");
240     incrIndent();
241     return true;
242 }
243 
exportTableRow(SqlResultsRowPtr data)244 bool XmlExport::exportTableRow(SqlResultsRowPtr data)
245 {
246     return exportQueryResultsRow(data);
247 }
248 
afterExportTable()249 bool XmlExport::afterExportTable()
250 {
251     decrIndent();
252     writeln("</rows>");
253     decrIndent();
254     writeln("</table>");
255     return true;
256 }
257 
beforeExportDatabase(const QString & database)258 bool XmlExport::beforeExportDatabase(const QString& database)
259 {
260     setupConfig();
261     write(docBegin.arg(codecName));
262 
263     writeln(QString("<database%1>").arg(nsStr));
264     incrIndent();
265     writeTagWithValue("name", database);
266 
267     return true;
268 }
269 
exportIndex(const QString & database,const QString & name,const QString & ddl,SqliteCreateIndexPtr createIndex)270 bool XmlExport::exportIndex(const QString& database, const QString& name, const QString& ddl, SqliteCreateIndexPtr createIndex)
271 {
272     writeln("<index>");
273     incrIndent();
274 
275     writeTagWithValue("database", database);
276     writeTagWithValue("name", name);
277     if (createIndex->uniqueKw)
278         writeln("<unique>true</unique>");
279 
280     if (createIndex->where)
281         writeTagWithValue("partial", createIndex->where->detokenize());
282 
283     writeTagWithValue("ddl", ddl);
284 
285     decrIndent();
286     writeln("</index>");
287     return true;
288 }
289 
exportTrigger(const QString & database,const QString & name,const QString & ddl,SqliteCreateTriggerPtr createTrigger)290 bool XmlExport::exportTrigger(const QString& database, const QString& name, const QString& ddl, SqliteCreateTriggerPtr createTrigger)
291 {
292     UNUSED(createTrigger);
293     writeln("<trigger>");
294     incrIndent();
295 
296     writeTagWithValue("database", database);
297     writeTagWithValue("name", name);
298     writeTagWithValue("ddl", ddl);
299 
300     QString timing = SqliteCreateTrigger::time(createTrigger->eventTime);
301     writeTagWithValue("timing", timing);
302 
303     QString event = createTrigger->event ? SqliteCreateTrigger::Event::typeToString(createTrigger->event->type) : "";
304     writeTagWithValue("action", event);
305 
306     QString tag;
307     if (createTrigger->eventTime == SqliteCreateTrigger::Time::INSTEAD_OF)
308         tag = "<%1view>";
309     else
310         tag = "<%1table>";
311 
312     writeln(tag.arg("") + escape(createTrigger->table) + tag.arg("/"));
313 
314     if (createTrigger->precondition)
315         writeTagWithValue("precondition", createTrigger->precondition->detokenize());
316 
317     QStringList queryStrings;
318     for (SqliteQuery* q : createTrigger->queries)
319         queryStrings << q->detokenize();
320 
321     writeTagWithValue("code", queryStrings.join("\n"));
322 
323     decrIndent();
324     writeln("</trigger>");
325     return true;
326 }
327 
exportView(const QString & database,const QString & name,const QString & ddl,SqliteCreateViewPtr createView)328 bool XmlExport::exportView(const QString& database, const QString& name, const QString& ddl, SqliteCreateViewPtr createView)
329 {
330     UNUSED(createView);
331     writeln("<view>");
332     incrIndent();
333 
334     writeTagWithValue("database", database);
335     writeTagWithValue("name", name);
336     writeTagWithValue("ddl", ddl);
337     writeTagWithValue("select", createView->select->detokenize());
338 
339     decrIndent();
340     writeln("</view>");
341     return true;
342 }
343 
afterExportDatabase()344 bool XmlExport::afterExportDatabase()
345 {
346     decrIndent();
347     writeln("</database>");
348     return true;
349 }
350 
setupConfig()351 void XmlExport::setupConfig()
352 {
353     codecName = codec->name();
354     indentDepth = 0;
355     newLineStr = "";
356     indentStr = "";
357     indent = (cfg.XmlExport.Format.get() == "format");
358     if (indent)
359         newLineStr = "\n";
360 
361     nsStr = QString();
362     if (cfg.XmlExport.UseNamespace.get())
363         nsStr = " xmlns=\"" + cfg.XmlExport.Namespace.get() + "\"";
364 
365     if (cfg.XmlExport.Escaping.get() == "ampersand")
366     {
367         useAmpersand = true;
368         useCdata = false;
369     }
370     else if (cfg.XmlExport.Escaping.get() == "cdata")
371     {
372         useAmpersand = false;
373         useCdata = true;
374     }
375     else
376     {
377         useAmpersand = true;
378         useCdata = true;
379     }
380 }
381 
incrIndent()382 void XmlExport::incrIndent()
383 {
384     if (indent)
385     {
386         indentDepth++;
387         updateIndent();
388     }
389 }
390 
decrIndent()391 void XmlExport::decrIndent()
392 {
393     if (indent)
394     {
395         indentDepth--;
396         updateIndent();
397     }
398 }
399 
updateIndent()400 void XmlExport::updateIndent()
401 {
402     indentStr = QString("    ").repeated(indentDepth);
403 }
404 
writeln(const QString & str)405 void XmlExport::writeln(const QString& str)
406 {
407     QString newStr;
408     if (str.contains("\n"))
409     {
410         QStringList lines = str.split("\n");
411         QMutableStringListIterator it(lines);
412         while (it.hasNext())
413             it.next().prepend(indentStr);
414 
415         newStr = lines.join("\n") + newLineStr;
416     }
417     else
418     {
419         newStr = indentStr + str + newLineStr;
420     }
421     GenericExportPlugin::write(newStr);
422 }
423 
escape(const QString & str)424 QString XmlExport::escape(const QString& str)
425 {
426     if (useAmpersand && useCdata)
427     {
428         if (str.length() >= minLenghtForCdata)
429             return escapeCdata(str);
430         else
431             return escapeAmpersand(str);
432     }
433     else if (useAmpersand)
434     {
435         return escapeAmpersand(str);
436     }
437     else
438     {
439         return escapeCdata(str);
440     }
441 }
442 
escapeCdata(const QString & str)443 QString XmlExport::escapeCdata(const QString& str)
444 {
445     static_qstring(tpl, "<![CDATA[%1]]>");
446     if (str.contains('"') || str.contains('&') || str.contains('<') || str.contains('>'))
447     {
448         int idx = str.indexOf("]]>");
449         if (idx > -1)
450             return escape(str.left(idx + 2)) + escape(str.mid(idx + 2));
451 
452         return tpl.arg(str);
453     }
454 
455     return str;
456 }
457 
escapeAmpersand(const QString & str)458 QString XmlExport::escapeAmpersand(const QString& str)
459 {
460     return str.toHtmlEscaped();
461 }
462 
tagWithValue(const QString & tag,const QString & value)463 QString XmlExport::tagWithValue(const QString& tag, const QString& value)
464 {
465     static_qstring(tpl, "<%1>%2</%1>");
466     return tpl.arg(tag, escape(value));
467 }
468 
writeTagWithValue(const QString & tag,const QString & value)469 void XmlExport::writeTagWithValue(const QString& tag, const QString& value)
470 {
471     writeln(tagWithValue(tag, value));
472 }
473 
toString(bool value)474 QString XmlExport::toString(bool value)
475 {
476     return value ? "true" : "false";
477 }
478 
init()479 bool XmlExport::init()
480 {
481     Q_INIT_RESOURCE(xmlexport);
482     return GenericExportPlugin::init();
483 }
484 
deinit()485 void XmlExport::deinit()
486 {
487     Q_CLEANUP_RESOURCE(xmlexport);
488 }
489