TD: move all XML query handling to a single function

This commit is contained in:
wmayer
2023-03-18 14:48:03 +01:00
committed by WandererFan
parent c7d637d677
commit 6fb167582a
7 changed files with 213 additions and 128 deletions

View File

@@ -175,6 +175,8 @@ SET(TechDraw_SRCS
TechDrawExport.h
ProjectionAlgos.cpp
ProjectionAlgos.h
XMLQuery.cpp
XMLQuery.h
)
SET(Geometry_SRCS

View File

@@ -26,10 +26,7 @@
#ifndef _PreComp_
# include <sstream>
# include <QDomDocument>
# include <QDomNodeModel.h>
# include <QFile>
# include <QXmlQuery>
# include <QXmlResultItems>
#endif
#include <App/Application.h>
@@ -44,6 +41,7 @@
#include "DrawSVGTemplate.h"
#include "DrawSVGTemplatePy.h"
#include "DrawUtil.h"
#include "XMLQuery.h"
using namespace TechDraw;
@@ -113,72 +111,64 @@ QString DrawSVGTemplate::processTemplate()
return QString();
}
QDomDocument templateDocument;
if (!templateDocument.setContent(&templateFile)) {
QDomDocument templateDocument;
if (!templateDocument.setContent(&templateFile)) {
Base::Console().Error("DrawSVGTemplate::processTemplate - failed to parse file: %s\n",
PageResult.getValue());
return QString();
}
return QString();
}
QXmlQuery query(QXmlQuery::XQuery10);
QDomNodeModel model(query.namePool(), templateDocument);
query.setFocus(QXmlItem(model.fromDomNode(templateDocument.documentElement())));
XMLQuery query(templateDocument);
std::map<std::string, std::string> substitutions = EditableTexts.getValues();
// XPath query to select all <tspan> nodes whose <text> parent
// has "freecad:editable" attribute
query.setQuery(QString::fromUtf8(
"declare default element namespace \"" SVG_NS_URI "\"; "
"declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; "
"//text[@freecad:editable]/tspan"));
// XPath query to select all <tspan> nodes whose <text> parent
// has "freecad:editable" attribute
query.processItems(QString::fromUtf8(
"declare default element namespace \"" SVG_NS_URI "\"; "
"declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; "
"//text[@freecad:editable]/tspan"),
[&substitutions, &templateDocument](QDomElement& tspan) -> bool {
// Replace the editable text spans with new nodes holding actual values
QString editableName = tspan.parentNode().toElement().attribute(QString::fromUtf8("freecad:editable"));
std::map<std::string, std::string>::iterator item =
substitutions.find(editableName.toStdString());
if (item != substitutions.end()) {
// Keep all spaces in the text node
tspan.setAttribute(QString::fromUtf8("xml:space"), QString::fromUtf8("preserve"));
QXmlResultItems queryResult;
query.evaluateTo(&queryResult);
// Remove all child nodes and append text node with editable replacement as the only descendant
while (!tspan.lastChild().isNull()) {
tspan.removeChild(tspan.lastChild());
}
tspan.appendChild(templateDocument.createTextNode(QString::fromUtf8(item->second.c_str())));
}
return true;
});
std::map<std::string, std::string> substitutions = EditableTexts.getValues();
while (!queryResult.next().isNull())
{
QDomElement tspan = model.toDomNode(queryResult.current().toNodeModelIndex()).toElement();
// Calculate the dimensions of the page and store for retrieval
// Obtain the size of the SVG document by reading the document attributes
QDomElement docElement = templateDocument.documentElement();
Base::Quantity quantity;
// Replace the editable text spans with new nodes holding actual values
QString editableName = tspan.parentNode().toElement().attribute(QString::fromUtf8("freecad:editable"));
std::map<std::string, std::string>::iterator item =
substitutions.find(std::string(editableName.toUtf8().constData()));
if (item != substitutions.end()) {
// Keep all spaces in the text node
tspan.setAttribute(QString::fromUtf8("xml:space"), QString::fromUtf8("preserve"));
// Obtain the width
QString str = docElement.attribute(QString::fromLatin1("width"));
quantity = Base::Quantity::parse(str);
quantity.setUnit(Base::Unit::Length);
// Remove all child nodes and append text node with editable replacement as the only descendant
while (!tspan.lastChild().isNull()) {
tspan.removeChild(tspan.lastChild());
}
tspan.appendChild(templateDocument.createTextNode(QString::fromUtf8(item->second.c_str())));
}
}
Width.setValue(quantity.getValue());
// Calculate the dimensions of the page and store for retrieval
// Obtain the size of the SVG document by reading the document attributes
QDomElement docElement = templateDocument.documentElement();
Base::Quantity quantity;
str = docElement.attribute(QString::fromLatin1("height"));
quantity = Base::Quantity::parse(str);
quantity.setUnit(Base::Unit::Length);
// Obtain the width
QString str = docElement.attribute(QString::fromLatin1("width"));
quantity = Base::Quantity::parse(str);
quantity.setUnit(Base::Unit::Length);
Height.setValue(quantity.getValue());
Width.setValue(quantity.getValue());
bool isLandscape = getWidth() / getHeight() >= 1.;
str = docElement.attribute(QString::fromLatin1("height"));
quantity = Base::Quantity::parse(str);
quantity.setUnit(Base::Unit::Length);
Orientation.setValue(isLandscape ? 1 : 0);
Height.setValue(quantity.getValue());
bool isLandscape = getWidth() / getHeight() >= 1.;
Orientation.setValue(isLandscape ? 1 : 0);
//all Qt holds on files should be released on exit #4085
return templateDocument.toString();
//all Qt holds on files should be released on exit #4085
return templateDocument.toString();
}
double DrawSVGTemplate::getWidth() const
@@ -218,7 +208,7 @@ std::map<std::string, std::string> DrawSVGTemplate::getEditableTextsFromTemplate
Base::FileInfo tfi(templateFilename);
if (!tfi.isReadable()) {
// if there is a old absolute template file set use a redirect
// if there is an old absolute template file set use a redirect
tfi.setFile(App::Application::getResourceDir() + "Mod/Drawing/Templates/" + tfi.fileName());
// try the redirect
if (!tfi.isReadable()) {
@@ -240,29 +230,22 @@ std::map<std::string, std::string> DrawSVGTemplate::getEditableTextsFromTemplate
return editables;
}
QXmlQuery query(QXmlQuery::XQuery10);
QDomNodeModel model(query.namePool(), templateDocument, true);
query.setFocus(QXmlItem(model.fromDomNode(templateDocument.documentElement())));
XMLQuery query(templateDocument);
// XPath query to select all <tspan> nodes whose <text> parent
// has "freecad:editable" attribute
query.setQuery(QString::fromUtf8(
query.processItems(QString::fromUtf8(
"declare default element namespace \"" SVG_NS_URI "\"; "
"declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; "
"//text[@freecad:editable]/tspan"));
QXmlResultItems queryResult;
query.evaluateTo(&queryResult);
while (!queryResult.next().isNull()) {
QDomElement tspan = model.toDomNode(queryResult.current().toNodeModelIndex()).toElement();
"//text[@freecad:editable]/tspan"),
[&editables](QDomElement& tspan) -> bool {
QString editableName = tspan.parentNode().toElement().attribute(QString::fromUtf8("freecad:editable"));
QString editableValue = tspan.firstChild().nodeValue();
editables[std::string(editableName.toUtf8().constData())] =
std::string(editableValue.toUtf8().constData());
}
return true;
});
return editables;
}

View File

@@ -23,11 +23,7 @@
#include "PreCompiled.h"
#ifndef _PreComp_
# include <sstream>
# include "QDomNodeModel.h"
# include <QDomDocument>
# include <QXmlResultItems>
# include <QXmlQuery>
#endif
#include <Base/Console.h>
@@ -36,6 +32,7 @@
#include "DrawViewSymbolPy.h" // generated from DrawViewSymbolPy.xml
#include "DrawPage.h"
#include "DrawUtil.h"
#include "XMLQuery.h"
using namespace TechDraw;
@@ -105,30 +102,22 @@ bool DrawViewSymbol::checkFit(TechDraw::DrawPage* p) const
std::vector<std::string> DrawViewSymbol::getEditableFields()
{
QDomDocument symbolDocument;
QXmlResultItems queryResult;
std::vector<std::string> editables;
bool rc = loadQDomDocument(symbolDocument);
if (rc) {
QDomElement symbolDocElem = symbolDocument.documentElement();
QXmlQuery query(QXmlQuery::XQuery10);
QDomNodeModel model(query.namePool(), symbolDocument);
query.setFocus(QXmlItem(model.fromDomNode(symbolDocument.documentElement())));
XMLQuery query(symbolDocument);
// XPath query to select all <tspan> nodes whose <text> parent
// has "freecad:editable" attribute
query.setQuery(QString::fromUtf8("declare default element namespace \"" SVG_NS_URI "\"; "
"declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; "
"//text[@freecad:editable]/tspan"));
query.evaluateTo(&queryResult);
while (!queryResult.next().isNull()) {
QDomElement tspan =
model.toDomNode(queryResult.current().toNodeModelIndex()).toElement();
query.processItems(QString::fromUtf8("declare default element namespace \"" SVG_NS_URI "\"; "
"declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; "
"//text[@freecad:editable]/tspan"),
[&editables](QDomElement& tspan) -> bool {
QString editableValue = tspan.firstChild().nodeValue();
editables.emplace_back(editableValue.toUtf8().constData());
}
editables.emplace_back(editableValue.toStdString());
return true;
});
}
return editables;
}
@@ -142,27 +131,22 @@ void DrawViewSymbol::updateFieldsInSymbol()
}
QDomDocument symbolDocument;
QXmlResultItems queryResult;
bool rc = loadQDomDocument(symbolDocument);
if (rc) {
QDomElement symbolDocElem = symbolDocument.documentElement();
QXmlQuery query(QXmlQuery::XQuery10);
QDomNodeModel model(query.namePool(), symbolDocument);
query.setFocus(QXmlItem(model.fromDomNode(symbolDocElem)));
XMLQuery query(symbolDocument);
std::size_t count = 0;
// XPath query to select all <tspan> nodes whose <text> parent
// has "freecad:editable" attribute
query.setQuery(QString::fromUtf8("declare default element namespace \"" SVG_NS_URI "\"; "
"declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; "
"//text[@freecad:editable]/tspan"));
query.evaluateTo(&queryResult);
unsigned int count = 0;
while (!queryResult.next().isNull()) {
QDomElement tspanElement =
model.toDomNode(queryResult.current().toNodeModelIndex()).toElement();
query.processItems(QString::fromUtf8("declare default element namespace \"" SVG_NS_URI "\"; "
"declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; "
"//text[@freecad:editable]/tspan"),
[&symbolDocument, &editText, &count](QDomElement& tspanElement) -> bool {
if (count >= editText.size()) {
return false;
}
// Keep all spaces in the text node
tspanElement.setAttribute(QString::fromUtf8("xml:space"),
QString::fromUtf8("preserve"));
@@ -174,9 +158,11 @@ void DrawViewSymbol::updateFieldsInSymbol()
// Finally append text node with editable replacement as the only <tspan> descendant
tspanElement.appendChild(
symbolDocument.createTextNode(QString::fromUtf8(editText[count].c_str())));
symbolDocument.createTextNode(QString::fromStdString(editText[count])));
++count;
}
return true;
});
Symbol.setValue(symbolDocument.toString(1).toStdString());
}
}

View File

@@ -28,6 +28,7 @@
#include <QDomDocument>
#include <QDomNode>
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
#include "QDomNodeModel.h"
#include <QSourceLocation>
#include <QUrl>
@@ -359,3 +360,4 @@ QXmlNodeModelIndex QDomNodeModel::nextFromSimpleAxis ( SimpleAxis axis, const QX
return QXmlNodeModelIndex();
}
#endif

View File

@@ -0,0 +1,75 @@
/***************************************************************************
* Copyright (c) 2023 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
# include <QDomDocument>
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
# include "QDomNodeModel.h"
# include <QXmlQuery>
# include <QXmlResultItems>
#endif
#endif
#include "XMLQuery.h"
using namespace TechDraw;
XMLQuery::XMLQuery(QDomDocument& dom)
: domDocument(dom)
{
}
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
bool XMLQuery::processItems(const QString& queryStr, const std::function<bool(QDomElement&)>& process)
{
QXmlQuery query(QXmlQuery::XQuery10);
QDomNodeModel model(query.namePool(), domDocument);
QDomElement symbolDocElem = domDocument.documentElement();
query.setFocus(QXmlItem(model.fromDomNode(symbolDocElem)));
query.setQuery(queryStr);
QXmlResultItems queryResult;
query.evaluateTo(&queryResult);
while (!queryResult.next().isNull()) {
QDomElement tspanElement =
model.toDomNode(queryResult.current().toNodeModelIndex()).toElement();
if (!process(tspanElement)) {
return false;
}
}
return true;
}
#else
bool XMLQuery::processItems(const QString& queryStr, const std::function<bool(QDomElement&)>& process)
{
//TODO: Port to Qt6
Q_UNUSED(queryStr)
Q_UNUSED(process)
return false;
}
#endif

View File

@@ -0,0 +1,49 @@
/***************************************************************************
* Copyright (c) 2023 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#ifndef TECHDRAW_XMLQuery_h_
#define TECHDRAW_XMLQuery_h_
#include <Mod/TechDraw/TechDrawGlobal.h>
#include <QList>
QT_BEGIN_NAMESPACE
class QDomDocument;
class QDomElement;
QT_END_NAMESPACE
namespace TechDraw
{
class TechDrawExport XMLQuery
{
public:
XMLQuery(QDomDocument&);
bool processItems(const QString& queryStr, const std::function<bool(QDomElement&)>& process);
private:
QDomDocument& domDocument;
};
} //namespace TechDraw
#endif //TECHDRAW_XMLQuery_h_

View File

@@ -29,8 +29,6 @@
# include <QGraphicsSvgItem>
# include <QPen>
# include <QSvgRenderer>
# include <QXmlQuery>
# include <QXmlResultItems>
#endif// #ifndef _PreComp_
#include <App/Application.h>
@@ -39,7 +37,7 @@
#include <Mod/TechDraw/App/DrawSVGTemplate.h>
#include <Mod/TechDraw/App/DrawUtil.h>
#include <Mod/TechDraw/App/QDomNodeModel.h>
#include <Mod/TechDraw/App/XMLQuery.h>
#include "QGISVGTemplate.h"
#include "PreferencesGui.h"
@@ -165,20 +163,6 @@ void QGISVGTemplate::createClickHandles()
}
file.close();
QDomElement templateDocElem = templateDocument.documentElement();
QXmlQuery query(QXmlQuery::XQuery10);
QDomNodeModel model(query.namePool(), templateDocument);
query.setFocus(QXmlItem(model.fromDomNode(templateDocElem)));
// XPath query to select all <text> nodes with "freecad:editable" attribute
query.setQuery(QString::fromUtf8("declare default element namespace \"" SVG_NS_URI "\"; "
"declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; "
"//text[@freecad:editable]"));
QXmlResultItems queryResult;
query.evaluateTo(&queryResult);
//TODO: Find location of special fields (first/third angle) and make graphics items for them
Base::Reference<ParameterGrp> hGrp = App::GetApplication()
@@ -194,10 +178,13 @@ void QGISVGTemplate::createClickHandles()
double width = editClickBoxSize;
double height = editClickBoxSize;
while (!queryResult.next().isNull()) {
QDomElement textElement =
model.toDomNode(queryResult.current().toNodeModelIndex()).toElement();
TechDraw::XMLQuery query(templateDocument);
// XPath query to select all <text> nodes with "freecad:editable" attribute
query.processItems(QString::fromUtf8("declare default element namespace \"" SVG_NS_URI "\"; "
"declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; "
"//text[@freecad:editable]"),
[&](QDomElement& textElement) -> bool {
QString name = textElement.attribute(QString::fromUtf8("freecad:editable"));
double x = Rez::guiX(
textElement.attribute(QString::fromUtf8("x"), QString::fromUtf8("0.0")).toDouble());
@@ -207,7 +194,7 @@ void QGISVGTemplate::createClickHandles()
if (name.isEmpty()) {
Base::Console().Warning(
"QGISVGTemplate::createClickHandles - no name for editable text at %f, %f\n", x, y);
continue;
return true;
}
auto item(new TemplateTextField(this, svgTemplate, name.toStdString()));
@@ -229,7 +216,8 @@ void QGISVGTemplate::createClickHandles()
addToGroup(item);
textFields.push_back(item);
}
return true;
});
}
#include <Mod/TechDraw/Gui/moc_QGISVGTemplate.cpp>