Jamoma API  0.6.0.a19
TTXmlHandler.cpp
Go to the documentation of this file.
1 /** @file
2  *
3  * @ingroup modularLibrary
4  *
5  * @brief TTObjectBase to handle xml file reading and writing to be able to store / recall state of an object into/from xml files.
6  *
7  * @details
8  *
9  * @authors Théo de la Hogue
10  *
11  * @copyright © 2010, Théo de la Hogue @n
12  * This code is licensed under the terms of the "New BSD License" @n
13  * http://creativecommons.org/licenses/BSD/
14  */
15 
16 
17 #include "TTXmlHandler.h"
18 #include <libxml/encoding.h>
19 #include <libxml/xmlwriter.h>
20 #include <libxml/xmlreader.h>
21 
22 #define thisTTClass TTXmlHandler
23 #define thisTTClassName "XmlHandler"
24 #define thisTTClassTags "xml, handler"
25 
26 TT_MODULAR_CONSTRUCTOR,
27 mFilePath(kTTSymEmpty),
28 mHeaderNodeName(TTSymbol("jamoma")),
29 mVersion(TTSymbol("0.6")),
30 mXmlSchemaInstance(TTSymbol("http://www.w3.org/2001/XMLSchema-instance")),
31 mXmlSchemaLocation(TTSymbol("http://jamoma.org/ file:jamoma.xsd")),
32 mWriter(NULL),
33 mReader(NULL),
34 mXmlNodeName(kTTSymEmpty),
35 mIsWriting(false),
36 mIsReading(false)
37 {
39 
40  addAttribute(HeaderNodeName, kTypeSymbol);
41  addAttribute(Version, kTypeSymbol);
42  addAttribute(XmlSchemaLocation, kTypeSymbol);
43 
46 
47  addMessage(WriteAgain);
48  addMessage(ReadAgain);
49 }
50 
51 TTXmlHandler::~TTXmlHandler()
52 {
53  ;
54 }
55 
57 {
58  mObject = value;
59 
60  return kTTErrNone;
61 }
62 
63 TTErr TTXmlHandler::Write(const TTValue& args, TTValue& outputValue)
64 {
65  TTValue v, objectValue, errValue, none;
66  TTObject anObject;
67  int ret;
68  TTUInt32 i;
69  TTErr err;
70 
71  // at least an object have to be selected
72  if (mObject.size() == 0)
73  return kTTErrGeneric;
74 
75  // memorize object value because it could change if the handler is used recursively
76  objectValue = mObject;
77 
78  // prepare error value
79  errValue.resize(objectValue.size());
80 
81  for (i = 0; i < errValue.size(); i++)
82  errValue[i] = kTTErrNone;
83 
84  // if the first argument is kTypeSymbol : this is an *absolute* file path
85  // start an xml file reading from the given file
86  if (args.size() == 1) {
87 
88  if (args[0].type() == kTypeSymbol) {
89 
90  mFilePath = args[0];
91 
92  // Init the xml library
93  LIBXML_TEST_VERSION
94 
95  // Create a new XmlWriter for filePath, with no compression.
96  mWriter = xmlNewTextWriterFilename(mFilePath.c_str(), 0);
97  if (mWriter == NULL) {
98  TT_ASSERT("TTXmlHandler::Write : Error creating the xml writer\n", true);
99  return kTTErrGeneric;
100  }
101 
102  // Start the document with the xml default for the version, the choosen encoding and yes for standalone status
103  ret = xmlTextWriterStartDocument((xmlTextWriterPtr)mWriter, NULL, TTMODULAR_XML_ENCODING, "yes");
104  if (ret < 0) {
105  TT_ASSERT("TTXmlHandler::Write : Error at xmlTextWriterStartDocument\n", true);
106  return kTTErrGeneric;
107  }
108 
109  mIsWriting = true;
110 
111  // to write a human readable file
112  xmlTextWriterSetIndent((xmlTextWriterPtr)mWriter, 1);
113 
114  // Start Header information
115  xmlTextWriterStartElement((xmlTextWriterPtr)mWriter, BAD_CAST mHeaderNodeName.c_str());
116  xmlTextWriterWriteAttribute((xmlTextWriterPtr)mWriter, BAD_CAST "version", BAD_CAST mVersion.c_str());
117  xmlTextWriterWriteAttribute((xmlTextWriterPtr)mWriter, BAD_CAST "xmlns:xsi", BAD_CAST mXmlSchemaInstance.c_str());
118  xmlTextWriterWriteAttribute((xmlTextWriterPtr)mWriter, BAD_CAST "xsi:schemaLocation", BAD_CAST mXmlSchemaLocation.c_str());
119 
120  // Write data for each given object (which have to implement a WriteAsXml message)
121  for (i = 0; i < objectValue.size(); i++) {
122 
123  if (objectValue[i].type() == kTypeObject && errValue[i] == kTTErrNone) {
124 
125  anObject = objectValue[i];
126  errValue[i] = anObject.send("WriteAsXml", TTObject(this), none);
127 
128  if (!(errValue[i] == kTTErrNone))
129  err = kTTErrGeneric;
130  }
131  }
132 
133  // End Header information
134  xmlTextWriterEndElement((xmlTextWriterPtr)mWriter);
135 
136  /* Here we could close the elements ORDER and EXAMPLE using the
137  * function xmlTextWriterEndElement, but since we do not want to
138  * write any other elements, we simply call xmlTextWriterEndDocument,
139  * which will do all the work. */
140  xmlTextWriterEndDocument((xmlTextWriterPtr)mWriter);
141 
142  xmlFreeTextWriter((xmlTextWriterPtr)mWriter);
143 
144  mIsWriting = false;
145 
146  // memorize the objectValue as the last handled object
147  mObject = objectValue;
148 
149  return err;
150  }
151  }
152 
153  // else write data for each given object (which have to implement a WriteAsXml message)
154  for (i = 0; i < objectValue.size(); i++) {
155 
156  if (objectValue[i].type() == kTypeObject && errValue[i] == kTTErrNone) {
157 
158  anObject = objectValue[i];
159  errValue[i] = anObject.send("WriteAsXml", TTObject(this), none);
160  }
161 
162  if (!(errValue[i] == kTTErrNone))
163  err = kTTErrGeneric;
164  }
165 
166  return err;
167 }
168 
169 TTErr TTXmlHandler::WriteAgain()
170 {
171  TTValue args;
172  TTValue dummy;
173 
174  args.append(mFilePath);
175  return Write(args, dummy);
176 }
177 
178 TTErr TTXmlHandler::Read(const TTValue& args, TTValue& outputValue)
179 {
180  TTUInt8 xType;
181  const xmlChar *xName = 0;
182  const xmlChar *xValue = 0;
183  TTObject anObject;
184  TTSymbol lastNodeName;
185  TTValue v, objectValue, errValue, none;
186  int ret;
187  TTUInt32 i;
188 
189  // at least an object have to be selected
190  if (mObject.size() == 0)
191  return kTTErrGeneric;
192 
193  // memorize object value because it could change if the handler is used recursively
194  objectValue = mObject;
195 
196  // prepare error value
197  errValue.resize(objectValue.size());
198 
199  for (i = 0; i < errValue.size(); i++)
200  errValue[i] = kTTErrNone;
201 
202  // if the first argument is kTypeSymbol : this is an *absolute* file path
203  // start an xml file reading from the given file
204  if (args.size() == 1) {
205 
206  if (args[0].type() == kTypeSymbol) {
207 
208  mFilePath = args[0];
209 
210  // Init the xml library
211  LIBXML_TEST_VERSION
212 
213  // Create a new XmlReader for filePath
214  mReader = xmlNewTextReaderFilename(mFilePath.c_str());
215  if (mReader == NULL) {
216  TT_ASSERT("TTXmlHandler::Read : Error creating the xml reader\n", true);
217  return kTTErrGeneric;
218  }
219 
220  if (mReader != NULL) {
221 
222  // Start reading
223  mIsReading = true;
224 
225  ret = xmlTextReaderRead((xmlTextReaderPtr)mReader);
226  while (ret == 1) {
227 
228  mXmlNodeIsEmpty = xmlTextReaderIsEmptyElement((xmlTextReaderPtr)mReader);
229 
230  // Get the type of the XML node
231  xType = xmlTextReaderNodeType((xmlTextReaderPtr)mReader);
232 
233  // Keep element (1) and comment (8) and end element (15) nodes
234  if (xType == 1 || xType == 8 || xType == 15) {
235 
236  switch (xType) {
237 
238  case 1: // For element node
239 
240  // Start to read a node
241  mXmlNodeStart = YES;
242 
243  // Get the node name
244  xName = xmlTextReaderName((xmlTextReaderPtr)mReader);
245  if (xName == NULL)
246  break;
247  mXmlNodeName = TTSymbol((char*)xName);
248  xmlFree((void*)xName);
249 
250  // to filter one line element
251  lastNodeName = mXmlNodeName;
252 
253  // replace header node name by start
255  mXmlNodeName = kTTSym_xmlHandlerReadingStarts;
256 
257  // Get the node value
258  xValue = xmlTextReaderReadString((xmlTextReaderPtr)mReader);
259  fromXmlChar(xValue, mXmlNodeValue);
260  break;
261 
262  case 15: // For end element node
263 
264  // Get the node name
265  xName = xmlTextReaderName((xmlTextReaderPtr)mReader);
266  if (xName == NULL)
267  break;
268  mXmlNodeName = TTSymbol((char*)xName);
269  xmlFree((void*)xName);
270 
271  // End to read a node
272  mXmlNodeStart = NO;
273 
274  // replace header node name by stop
276  mXmlNodeName = kTTSym_xmlHandlerReadingEnds;
277 
278  // Set the node value
280 
281  break;
282 
283  case 8: // For comment node
284 
285  // Set the node name
286  mXmlNodeName = kTTSym_xmlHandlerReadingComment;
287 
288  // Get the node value
289  xValue = xmlTextReaderValue((xmlTextReaderPtr)mReader);
290  fromXmlChar(xValue, mXmlNodeValue, YES);
291  break;
292 
293  default:
294  break;
295  }
296 
297  // Read data for each given object (which have to implement a ReadFromXml message)
298  for (i = 0; i < objectValue.size(); i++) {
299 
300  if (objectValue[i].type() == kTypeObject && errValue[i] == kTTErrNone) {
301 
302  anObject = objectValue[i];
303  errValue[i] = anObject.send("ReadFromXml", TTObject(this), none);
304  }
305  }
306  }
307 
308  // next node
309  ret = xmlTextReaderRead((xmlTextReaderPtr)mReader);
310  }
311 
312  if (ret != 0)
313  ;// TODO : failed to parse
314 
315  // End reading
316  xmlFreeTextReader((xmlTextReaderPtr)mReader);
317  mIsReading = false;
318 
319  // memorize the objectValue as the last handled object
320  mObject = objectValue;
321  }
322  else
323  return kTTErrGeneric;
324 
325  return kTTErrNone;
326  }
327  }
328 
329  // else read data for each given object (which have to implement a ReadFromXml message)
330  for (i = 0; i < objectValue.size(); i++) {
331 
332  if (objectValue[i].type() == kTypeObject) {
333 
334  if (objectValue[i].type() == kTypeObject && errValue[i] == kTTErrNone) {
335 
336  anObject = objectValue[i];
337  errValue[i] = anObject.send("ReadFromXml", TTObject(this), none);
338  }
339  }
340  }
341 
342  return kTTErrNone;
343 }
344 
345 TTErr TTXmlHandler::ReadAgain()
346 {
347  TTValue args;
348  TTValue dummy;
349 
350  args.append(mFilePath);
351  return Read(args, dummy);
352 }
353 
354 TTErr TTXmlHandler::fromXmlChar(const void* axCh, TTValue& v, TTBoolean addQuote, TTBoolean numberAsSymbol)
355 {
356  const xmlChar* xCh = (const xmlChar*)axCh;
357 
358  v.clear();
359 
360  if (xCh) {
361 
362  if (xCh[0] != '\n' && xCh[0] != '\0') {
363  TTString cString;
364 
365  if (addQuote) {
366  cString = "\"";
367  cString += (char*)xCh;
368  cString += "\"";
369  }
370  else
371  cString = (char*)xCh;
372 
373  v = cString;
374  v.fromString();
375  xmlFree((void*)xCh);
376  return kTTErrNone;
377  }
378  }
379 
380  return kTTErrGeneric;
381 }
382 
383 TTErr TTXmlHandler::getXmlAttribute(TTSymbol attributeName, TTValue& returnedValue, TTBoolean addQuote, TTBoolean numberAsSymbol)
384 {
385  if (xmlTextReaderMoveToAttribute((xmlTextReaderPtr)mReader, BAD_CAST attributeName.c_str()) == 1) {
386 
387  return fromXmlChar(xmlTextReaderValue((xmlTextReaderPtr)mReader), returnedValue, addQuote, numberAsSymbol);
388  }
389 
390  return kTTErrGeneric;
391 }
392 
393 TTErr TTXmlHandler::getXmlNextAttribute(TTSymbol& returnedAttributeName, TTValue& returnedValue, TTBoolean addQuote, TTBoolean numberAsSymbol)
394 {
395  TTValue v;
396 
397  if (xmlTextReaderMoveToNextAttribute((xmlTextReaderPtr)mReader) == 1) {
398 
399  fromXmlChar(xmlTextReaderName((xmlTextReaderPtr)mReader), v);
400 
401  if (v.size() == 1) {
402 
403  if (v[0].type() == kTypeSymbol) {
404 
405  returnedAttributeName = v[0];
406  return fromXmlChar(xmlTextReaderValue((xmlTextReaderPtr)mReader), returnedValue, addQuote, numberAsSymbol);
407  }
408  }
409  }
410 
411  return kTTErrGeneric;
412 }
TTErr setObject(const TTValue &value)
Setter for mObject attribute.
bool TTBoolean
Boolean flag, same as Boolean on the Mac.
Definition: TTBase.h:167
void fromString(TTBoolean numberAsSymbol=NO)
Convert a single string into individual elements using space to divide items.
Definition: TTValue.h:386
TTErr Write(const TTValue &args, TTValue &outputValue)
TTXmlWriter could takes absolute file path or nothing.
TTValue mXmlNodeValue
the Node value being read by the Reader
Definition: TTXmlHandler.h:68
TTErr send(const TTSymbol aName)
Send a message to this object with no arguments.
Definition: TTObject.cpp:135
TTErr fromXmlChar(const void *xCh, TTValue &v, TTBoolean addQuote=NO, TTBoolean numberAsSymbol=NO)
TTXmlReader make a TTValue from an xmlChar* using the fromString method (see in TTValue.h)
#define addAttribute(name, type)
A convenience macro to be used by subclasses for registering attributes with a custom getter...
Definition: TTAttribute.h:29
TTObjectBase to handle xml file reading and writing to be able to store / recall state of an object i...
Object type.
Definition: TTBase.h:283
Create and use Jamoma object instances.
Definition: TTObject.h:29
TTValue mObject
the last handled object (it is possible to have an array of objects)
Definition: TTXmlHandler.h:54
TTErr getXmlAttribute(TTSymbol attributeName, TTValue &returnedValue, TTBoolean addQuote=NO, TTBoolean numberAsSymbol=NO)
Get the value of an xml element attribute.
size_type size() const noexcept
Return the number of elements.
TTBoolean mXmlNodeIsEmpty
true if the Node is empty
Definition: TTXmlHandler.h:66
Symbol type.
Definition: TTBase.h:282
This is a special type used by TTAttribute to indicate that a value is a TTValue and is locally maint...
Definition: TTBase.h:286
void append(const T &anElementValueToAppend)
Insert a single TTElement at the end.
Definition: TTValue.h:243
TTSymbol mXmlSchemaLocation
the URL of the xml schema location
Definition: TTXmlHandler.h:60
TTSymbol mVersion
the version number
Definition: TTXmlHandler.h:58
TTErr getXmlNextAttribute(TTSymbol &returnedAttributeName, TTValue &returnedValue, TTBoolean addQuote=NO, TTBoolean numberAsSymbol=NO)
Get the value of the next xml element attribute.
The TTSymbol class is used to represent a string and efficiently pass and compare that string...
Definition: TTSymbol.h:26
TTSymbol mXmlSchemaInstance
the URL of the schema instance location
Definition: TTXmlHandler.h:59
TTSymbol mHeaderNodeName
the name of the header node in the xml file
Definition: TTXmlHandler.h:57
TTBoolean mXmlNodeStart
true if the Reader starts to read a Node
Definition: TTXmlHandler.h:65
#define addMessageWithArguments(name)
A convenience macro to be used by subclasses for registering messages.
Definition: TTMessage.h:27
const char * c_str() const
Return a pointer to the internal string as a C-string.
Definition: TTSymbol.h:77
void clear()
Clear all values from the vector, leaving with size of 0.
Definition: TTValue.h:131
Something went wrong, but what exactly is not known. Typically used for context-specific problems...
Definition: TTBase.h:344
TTErr
Jamoma Error Codes Enumeration of error codes that might be returned by any of the TTBlue functions a...
Definition: TTBase.h:342
TTSymbol mXmlNodeName
the Node name being read by the Reader
Definition: TTXmlHandler.h:67
std::uint32_t TTUInt32
32 bit unsigned integer
Definition: TTBase.h:178
#define addAttributeWithSetter(name, type)
A convenience macro to be used by subclasses for registering attributes with a custom setter...
Definition: TTAttribute.h:47
#define addMessage(name)
A convenience macro to be used by subclasses for registering messages.
Definition: TTMessage.h:19
No Error.
Definition: TTBase.h:343
The TTString class is used to represent a string.
Definition: TTString.h:34
TTSymbol mFilePath
the path to the last writen/read file
Definition: TTXmlHandler.h:55
TTErr Read(const TTValue &args, TTValue &outputValue)
TTXmlReader could takes absolute file path or nothing.
void resize(size_type n)
Change the number of elements.
[doxygenAppendixC_copyExample]
Definition: TTValue.h:34
unsigned char TTUInt8
8 bit unsigned integer (char)
Definition: TTBase.h:174