hydrogen 1.2.6
Xml.cpp
Go to the documentation of this file.
1/*
2 * Hydrogen
3 * Copyright(c) 2002-2008 by Alex >Comix< Cominu [comix@users.sourceforge.net]
4 * Copyright(c) 2008-2025 The hydrogen development team [hydrogen-devel@lists.sourceforge.net]
5 *
6 * http://www.hydrogen-music.org
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY, without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see https://www.gnu.org/licenses
20 *
21 */
22
23#include <core/Helpers/Xml.h>
24#include <core/Helpers/Legacy.h>
25
26#include <QtCore/QFile>
27#include <QtCore/QLocale>
28#include <QtCore/QString>
29#include <QtCore/QTextStream>
30
31#ifdef H2CORE_HAVE_QT6
32 #include <QStringConverter>
33#endif
34
35#define XMLNS_BASE "http://www.hydrogen-music.org/"
36#define XMLNS_XSI "http://www.w3.org/2001/XMLSchema-instance"
37
38namespace H2Core
39{
40
42XMLNode::XMLNode( QDomNode node ) : QDomNode( node ) { }
43
44XMLNode XMLNode::createNode( const QString& name )
45{
46 XMLNode node = this->ownerDocument().createElement( name );
47 appendChild( node );
48 return node;
49}
50
51QString XMLNode::read_child_node( const QString& node, bool inexistent_ok, bool empty_ok, bool bSilent )
52{
53 if( isNull() ) {
54 ERRORLOG( QString( "try to read %1 XML node from an empty parent %2." )
55 .arg( node ).arg( nodeName() ) );
56 return nullptr;
57 }
58 QDomElement el = firstChildElement( node );
59 if( el.isNull() ) {
60 if ( !inexistent_ok && ! bSilent ) {
61 WARNINGLOG( QString( "XML node %1->%2 should exists." )
62 .arg( nodeName() ).arg( node ) );
63 }
64 return nullptr;
65 }
66 if( el.text().isEmpty() ) {
67 if( !empty_ok && ! bSilent ) {
68 WARNINGLOG( QString( "XML node %1->%2 should not be empty." )
69 .arg( nodeName() ).arg( node ) );
70 }
71 return nullptr;
72 }
73 return el.text();
74}
75
76QString XMLNode::read_string( const QString& node, const QString& sDefaultValue, bool inexistent_ok, bool empty_ok, bool bSilent )
77{
78 QString sText = read_child_node( node, inexistent_ok, empty_ok, bSilent );
79 if ( sText.isNull() && ! sDefaultValue.isEmpty() ) {
80 if ( ! bSilent ) {
81 WARNINGLOG( QString( "Using default value %1 for %2" )
82 .arg( sDefaultValue ).arg( node ) );
83 }
84 return sDefaultValue;
85 }
86 return sText;
87}
88QColor XMLNode::read_color( const QString& node, const QColor& default_value, bool inexistent_ok, bool empty_ok, bool bSilent )
89{
90 QString text = read_child_node( node, inexistent_ok, empty_ok, bSilent );
91
92 if ( ! text.isEmpty() ) {
93 QStringList textList = text.split( QLatin1Char( ',' ) );
94 if ( textList.size() != 3 ) {
95 if ( ! bSilent ) {
96 WARNINGLOG( QString( "Invalid color format [%1] for node [%2]" )
97 .arg( default_value.name() ).arg( node ) );
98 }
99 return default_value;
100 }
101
102 QColor color( textList[ 0 ].toInt(), textList[ 1 ].toInt(), textList[ 2 ].toInt() );
103 if ( ! color.isValid() ) {
104 if ( ! bSilent ) {
105 WARNINGLOG( QString( "Invalid color values [%1] for node [%2]" )
106 .arg( default_value.name() ).arg( node ) );
107 }
108 return default_value;
109 }
110 return color;
111 }
112
113 if ( ! bSilent ) {
114 WARNINGLOG( QString( "Using default value [%1] for node [%2]" )
115 .arg( default_value.name() ).arg( node ) );
116 }
117 return default_value;
118}
119
120float XMLNode::read_float( const QString& node, float default_value, bool inexistent_ok, bool empty_ok, bool bSilent )
121{
122 QString ret = read_child_node( node, inexistent_ok, empty_ok, bSilent );
123 if( ret.isNull() ) {
124 if ( ! bSilent ) {
125 WARNINGLOG( QString( "Using default value %1 for %2" )
126 .arg( default_value ).arg( node ) );
127 }
128 return default_value;
129 }
130 QLocale c_locale = QLocale::c();
131 return c_locale.toFloat( ret );
132}
133
134float XMLNode::read_float( const QString& node, float default_value, bool *pFound, bool inexistent_ok, bool empty_ok, bool bSilent )
135{
136 QString ret = read_child_node( node, inexistent_ok, empty_ok, bSilent );
137 if( ret.isNull() ) {
138 if ( ! bSilent ) {
139 WARNINGLOG( QString( "Using default value %1 for %2" )
140 .arg( default_value ).arg( node ) );
141 }
142 *pFound = false;
143 return default_value;
144 } else {
145 *pFound = true;
146 QLocale c_locale = QLocale::c();
147 return c_locale.toFloat( ret );
148 }
149}
150
151int XMLNode::read_int( const QString& node, int default_value, bool inexistent_ok, bool empty_ok, bool bSilent )
152{
153 QString ret = read_child_node( node, inexistent_ok, empty_ok, bSilent );
154 if( ret.isNull() ) {
155 if ( ! bSilent ) {
156 WARNINGLOG( QString( "Using default value %1 for %2" )
157 .arg( default_value ).arg( node ) );
158 }
159 return default_value;
160 }
161 QLocale c_locale = QLocale::c();
162 return c_locale.toInt( ret );
163}
164
165bool XMLNode::read_bool( const QString& node, bool default_value, bool inexistent_ok, bool empty_ok, bool bSilent )
166{
167 QString ret = read_child_node( node, inexistent_ok, empty_ok, bSilent );
168 if( ret.isNull() ) {
169 if ( ! bSilent ) {
170 WARNINGLOG( QString( "Using default value %1 for %2" )
171 .arg( default_value ).arg( node ) );
172 }
173 return default_value;
174 }
175 if( ret=="true" ) {
176 return true;
177 } else {
178 return false;
179 }
180}
181
182bool XMLNode::read_bool( const QString& node, bool default_value, bool* pFound, bool inexistent_ok, bool empty_ok, bool bSilent )
183{
184 QString ret = read_child_node( node, inexistent_ok, empty_ok, bSilent );
185 if( ret.isNull() ) {
186 *pFound = false;
187 if ( ! bSilent ) {
188 WARNINGLOG( QString( "Using default value %1 for %2" )
189 .arg( default_value ).arg( node ) );
190 }
191 return default_value;
192 }
193
194 *pFound = true;
195 if( ret=="true" ) {
196 return true;
197 } else {
198 return false;
199 }
200}
201
202QString XMLNode::read_text( bool empty_ok, bool bSilent )
203{
204 QString text = toElement().text();
205 if ( !empty_ok && text.isEmpty() && ! bSilent ) {
206 WARNINGLOG( QString( "XML node %1 should not be empty." ).arg( nodeName() ) );
207 }
208 return text;
209}
210
211QString XMLNode::read_attribute( const QString& attribute, const QString& default_value, bool inexistent_ok, bool empty_ok, bool bSilent )
212{
213 QDomElement el = toElement();
214 if ( !inexistent_ok && !el.hasAttribute( attribute ) ) {
215 if ( ! bSilent ) {
216 WARNINGLOG( QString( "XML node %1 attribute %2 should exists." )
217 .arg( nodeName() ).arg( attribute ) );
218 }
219 return default_value;
220 }
221 QString attr = el.attribute( attribute );
222 if ( attr.isEmpty() ) {
223 if( !empty_ok && ! bSilent ) {
224 WARNINGLOG( QString( "XML node %1 attribute %2 should not be empty." )
225 .arg( nodeName() ).arg( attribute ) );
226 }
227
228 if ( ! bSilent ) {
229 WARNINGLOG( QString( "Using default value %1 for attribute %2" )
230 .arg( default_value ).arg( attribute ) );
231 }
232 return default_value;
233 }
234 return attr;
235}
236
237void XMLNode::write_attribute( const QString& attribute, const QString& value )
238{
239 toElement().setAttribute( attribute, value );
240}
241
242void XMLNode::write_child_node( const QString& node, const QString& text )
243{
244 QDomDocument doc = this->ownerDocument();
245 QDomElement el = doc.createElement( node );
246 QDomText txt = doc.createTextNode( text );
247 el.appendChild( txt );
248 this->appendChild( el );
249}
250void XMLNode::write_string( const QString& node, const QString& value )
251{
252 write_child_node( node, value );
253}
254void XMLNode::write_color( const QString& node, const QColor& color )
255{
256 write_child_node( node, QString( "%1,%2,%3" )
257 .arg( color.red() )
258 .arg( color.green() )
259 .arg( color.blue() ) );
260}
261void XMLNode::write_float( const QString& node, const float value )
262{
263 write_child_node( node, QString::number( value ) );
264}
265void XMLNode::write_int( const QString& node, const int value )
266{
267 write_child_node( node, QString::number( value ) );
268}
269void XMLNode::write_bool( const QString& name, const bool value )
270{
271 write_child_node( name, QString( ( value ? "true" : "false" ) ) );
272}
273
274
276
277bool XMLDoc::read( const QString& sFilePath, bool bSilent )
278{
279
280 QFile file( sFilePath );
281 if ( !file.open( QIODevice::ReadOnly ) ) {
282 ERRORLOG( QString( "Unable to open [%1] for reading" )
283 .arg( sFilePath ) );
284 return false;
285 }
286
287 if ( Legacy::checkTinyXMLCompatMode( &file ) ) {
288 // Document was created using TinyXML and not using QtXML. We
289 // need to convert it first.
290 if ( ! setContent( Legacy::convertFromTinyXML( &file ) ) ) {
291 ERRORLOG( QString( "Unable to read conversion result document [%1]" )
292 .arg( sFilePath ) );
293 file.close();
294 return false;
295 }
296 }
297 else {
298 // File was written using current format.
299 if ( ! setContent( &file ) ) {
300 ERRORLOG( QString( "Unable to read XML document [%1]" )
301 .arg( sFilePath ) );
302 file.close();
303 return false;
304 }
305 }
306 file.close();
307
308 return true;
309}
310
311bool XMLDoc::write( const QString& filepath )
312{
313 QFile file( filepath );
314 if ( !file.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) ) {
315 ERRORLOG( QString( "Unable to open %1 for writing" ).arg( filepath ) );
316 return false;
317 }
318 QTextStream out( &file );
319#ifdef H2CORE_HAVE_QT6
320 out.setEncoding( QStringConverter::Utf8 );
321#else
322 out.setCodec( "UTF-8" );
323#endif
324 out << toString().toUtf8();
325 out.flush();
326
327 bool rv = true;
328 if ( !toString().isEmpty() && file.size() == 0 ) {
329 rv = false;
330 }
331
332 file.close();
333 return rv;
334}
335
336XMLNode XMLDoc::set_root( const QString& node_name, const QString& xmlns )
337{
338 QDomProcessingInstruction header = createProcessingInstruction( "xml", "version=\"1.0\" encoding=\"UTF-8\"" );
339 appendChild( header );
340 XMLNode root = createElement( node_name );
341 if ( !xmlns.isEmpty() ) {
342 QDomElement el = root.toElement();
343 el.setAttribute( "xmlns",XMLNS_BASE+xmlns );
344 el.setAttribute( "xmlns:xsi",XMLNS_XSI );
345 }
346 appendChild( root );
347 return root;
348}
349
350};
#define WARNINGLOG(x)
Definition Object.h:241
#define ERRORLOG(x)
Definition Object.h:242
#define XMLNS_XSI
Definition Xml.cpp:36
#define XMLNS_BASE
Definition Xml.cpp:35
static bool checkTinyXMLCompatMode(QFile *pFile, bool bSilent=false)
Definition Legacy.cpp:334
static QByteArray convertFromTinyXML(QFile *pFile, bool bSilent=false)
Definition Legacy.cpp:356
XMLNode set_root(const QString &node_name, const QString &xmlns=nullptr)
create the xml header and root node
Definition Xml.cpp:336
bool read(const QString &filepath, bool bSilent=false)
read the content of an xml file
Definition Xml.cpp:277
bool write(const QString &filepath)
write itself into a file
Definition Xml.cpp:311
XMLDoc()
basic constructor
Definition Xml.cpp:275
XMLNode is a subclass of QDomNode with read and write values methods.
Definition Xml.h:39
int read_int(const QString &node, int default_value, bool inexistent_ok=true, bool empty_ok=true, bool bSilent=false)
reads an integer stored into a child node
Definition Xml.cpp:151
void write_attribute(const QString &attribute, const QString &value)
write a string as an attribute of the node
Definition Xml.cpp:237
QString read_child_node(const QString &node, bool inexistent_ok, bool empty_ok, bool bSilent=false)
reads a string stored into a child node
Definition Xml.cpp:51
bool read_bool(const QString &node, bool default_value, bool inexistent_ok=true, bool empty_ok=true, bool bSilent=false)
reads a boolean stored into a child node
Definition Xml.cpp:165
QString read_attribute(const QString &attribute, const QString &default_value, bool inexistent_ok, bool empty_ok, bool bSilent=false)
reads an attribute from the node
Definition Xml.cpp:211
QColor read_color(const QString &node, const QColor &defaultValue=QColor(97, 167, 251), bool inexistent_ok=true, bool empty_ok=true, bool bSilent=false)
Definition Xml.cpp:88
void write_child_node(const QString &node, const QString &text)
write a string into a child node
Definition Xml.cpp:242
QString read_string(const QString &node, const QString &default_value, bool inexistent_ok=true, bool empty_ok=true, bool bSilent=false)
reads a string stored into a child node
Definition Xml.cpp:76
float read_float(const QString &node, float default_value, bool inexistent_ok=true, bool empty_ok=true, bool bSilent=false)
reads a float stored into a child node
Definition Xml.cpp:120
void write_color(const QString &node, const QColor &color)
Definition Xml.cpp:254
void write_float(const QString &node, const float value)
write a float into a child node
Definition Xml.cpp:261
XMLNode createNode(const QString &name)
create a new XMLNode that has to be appended into de XMLDoc
Definition Xml.cpp:44
XMLNode()
basic constructor
Definition Xml.cpp:41
void write_string(const QString &node, const QString &value)
write a string into a child node
Definition Xml.cpp:250
void write_bool(const QString &node, const bool value)
write a boolean into a child node
Definition Xml.cpp:269
QString read_text(bool empty_ok, bool bSilent=false)
reads the text (content) from the node
Definition Xml.cpp:202
void write_int(const QString &node, const int value)
write an integer into a child node
Definition Xml.cpp:265