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