PLCnext API Documentation  21.9.0.40
XmlReader.hpp
1 //
3 // Copyright PHOENIX CONTACT Electronics GmbH
4 //
6 #pragma once
7 #include "Arp/System/Core/Arp.h"
8 #include "Arp/System/Core/Enum.hxx"
9 #include "Arp/System/Core/TypeName.hxx"
10 #include "Arp/System/Commons/Chrono/Duration.hpp"
11 #include "Arp/System/Commons/Exceptions/Exceptions.h"
12 #include "Arp/System/Commons/Xml/XmlErrorHandler.hpp"
13 #include "Arp/System/Commons/Xml/XmlNodeType.hpp"
14 
15 #include <sstream>
16 #include "boost/lexical_cast.hpp"
17 
18 namespace Arp { namespace System { namespace Commons { namespace Xml
19 {
20 
21 class LibXmlErrorHandler;
22 
24 class XmlReader
25 {
26 public: // typedefs
27 public: // construction/destruction
29  XmlReader(void);
31  XmlReader(XmlReader&& arg);
33  ~XmlReader(void);
34 
35 private: // construction/destruction
36  XmlReader(void* pReader);
37  XmlReader(const XmlReader& arg) = delete;
38  XmlReader& operator=(const XmlReader& arg) = delete;
39 
40 public: // static factory operations
47  static XmlReader Create(const String& filename);
48 
55  static bool TryCreate(const String& filename, XmlReader& reader);
56 
70  static XmlReader Create(const String& filename, const String& xsdFile);
71 
72 
80  static XmlReader CreateForString(const String& buffer);
81 
82 public: // setter/getter operations
90 
97  long GetLineNumber();
98 
105  const String& GetDocumentFileName();
106 
107 public: // operations
114  bool Read();
115 
122  XmlNodeType GetCurrentNodeType();
129  bool IsEmptyElement();
130 
137  int GetCurrentDepth();
145  bool Skip(); // TODO: remove this due to unclear naming
152  bool SkipElement();
153 
161  bool SkipElementContent();
162 
163  // moves to the element that contains the current attribute
164 
172  bool MoveToElement();
173  // moves to the next element with name
174 
184  bool ReadToFollowing(const String& name);
185  // reads to the next element with name included in current element
186 
198  bool ReadToDescendant(const String& name);
199  // skips the current element and reads until the next sibling with name
200 
211  bool ReadToNextSibling(const String& name);
212 
213  // reads to the next start element and tests the name against name, not needed after
214  // ReadEndElement
215 
223  void ReadStartElement(const char* elementName = nullptr);
224 
235  bool TryReadStartElement(const char* elementName = nullptr);
245  bool TryReadStartElement(String& elementName, bool conditioned = true);
246 
251  void ReadEndElement(void);
259  bool TryReadEndElement(void);
268  // reads the content as CData section of the current element
269  // read CDATA is not required, ReadElementContent removes CDATA Tags automatically
270  // String ReadElementContentCData(void);
271  // bool TryReadElementContentCData(String& result);
272 
280  template<class T>
282  {
283  return boost::lexical_cast<T>(ReadElementContent());
284  }
285 
297  template<class T>
298  T GetAttributeValue(const char* attributeName)
299  {
300  T returnValue;
301  if (TryGetAttributeValue<T>(attributeName, returnValue))
302  {
303  return returnValue;
304  }
305  else
306  {
307  throw XmlException(
308  "GetAttributeValue failed for mandatory attribute: {0} elementName: {1}",
309  attributeName, GetElementLocalName());
310  }
311  }
312 
324  template<class T>
325  Enum<T> GetAttributeValueEnum(const char* attributeName)
326  {
327  Enum<T> returnValue;
328 
329  if (TryGetAttributeValueEnum<T>(attributeName, returnValue))
330  {
331  return returnValue;
332  }
333  else
334  {
335  throw XmlException(
336  "GetAttributeValueEnum failed for mandatory attribute: {0} elementName: {1}",
337  attributeName, GetElementLocalName());
338  }
339  }
340 
355  template<class T>
356  bool TryGetAttributeValueEnum(const char* attributeName, Enum<T>& value)
357  {
358  char* charValue = GetAttributeValueInternal(attributeName);
359  if (charValue == nullptr)
360  {
361  return false;
362  }
363 
364  value = Enum<T>::Parse(charValue);
365  FreeInternalAttributeValue(charValue);
366  return true;
367  }
368 
386  template<class T>
387  bool TryGetAttributeValueEnum(const char* attributeName, T defaultValue, Enum<T>& value)
388  {
389  value = defaultValue;
390  return TryGetAttributeValueEnum<T>(attributeName, value);
391  }
392 
393 
406  template<class T>
407  T GetAttributeValueDuration(const char* attributeName)
408  {
409  T returnValue;
410 
411  if (TryGetAttributeValueDuration<T>(attributeName, returnValue))
412  {
413  return returnValue;
414  }
415  else
416  {
417  throw XmlException(
418  "GetAttributeValueDuration failed for mandatory attribute: {0} elementName: {1}",
419  attributeName, GetElementLocalName());
420  }
421  }
435  template<class T>
436  bool TryGetAttributeValueDuration(const char* attributeName, T& value)
437  {
438  char* charValue = GetAttributeValueInternal(attributeName);
439  Chrono::Duration tempDurationValue;
440 
441  if (charValue == nullptr)
442  {
443  return false;
444  }
445 
446  bool result = Chrono::Duration::TryParse(charValue, tempDurationValue);
447  if (!result)
448  {
449  throw XmlException(
450  "TryGetAttributeValueDuration failed for attribute '{0}' in element '{1}': invalid duration format in value '{2}'",
451  attributeName, GetElementLocalName(), charValue);
452  }
453  tempDurationValue.GetDuration(value);
454 
455  FreeInternalAttributeValue(charValue);
456  return result;
457  }
458 
476  template<class T>
477  bool TryGetAttributeValueDuration(const char* attributeName, T defaultValue, T& value)
478  {
479  value = defaultValue;
480  return TryGetAttributeValueDuration<T>(attributeName, value);
481  }
482 
483 
497  template<class T>
498  bool TryGetAttributeValue(const char* attributeName, T& value)
499  {
500  char* charValue = nullptr;
501  try
502  {
503  charValue = GetAttributeValueInternal(attributeName);
504  if (charValue == nullptr)
505  {
506  return false;
507  }
508 
509  // boost::lexical_cast seems fast enough:
510  // http://stackoverflow.com/questions/23582089/is-boostlexical-cast-redundant-with-c11-stoi-stof-and-family
511  // boost::lexical_cast is 10 times slower than atoi on Linux but has more error checks
512  // http://tinodidriksen.com/2010/02/16/cpp-convert-string-to-int-speed/
513  value = boost::lexical_cast<T>(charValue);
514  FreeInternalAttributeValue(charValue);
515  return true;
516  }
517  catch (boost::bad_lexical_cast&)
518  {
519 
520  String strValue(charValue);
521  FreeInternalAttributeValue(charValue);
522  throw XmlException("Cast of attribute {0} to {1} failed value: {2}", attributeName,
523  TypeName<T>(), strValue);
524  }
525  }
526 
527 
545  template<class T>
546  bool TryGetAttributeValue(const char* attributeName, T defaultValue, T& value)
547  {
548 
549  value = defaultValue;
550  return TryGetAttributeValue<T>(attributeName, value);
551  }
552 
553  void EnableValidationException();
554  void DisableValidationException();
555 
565  template<typename... Args>
566  XmlException CreateException(const char* message, const Args& ... args);
567 
568 protected: // operations
569 private: // static methods
570 private: // methods
571  int ReaderState();
572  char* GetAttributeValueInternal(const char* attributeName);
573  void FreeInternalAttributeValue(char* attributeValue);
574  // Move to the content node, if the element is not an content node
575  // content nodes are: non-whitespace text, CDATA, Element, EndElement, EntityReference,
576  // EndEntity
577  XmlNodeType MoveToContent();
578  void EnableValidation(const String& xsdFile);
579  void CheckForValidationException();
580 
581 private: // fields
582  String filename;
583  // internal reader ptr
584  void* reader;
585  bool validationEnabled;
586  LibXmlErrorHandler* errorHandler;
587  bool exceptionOnValidationError; // defines whether to throw an exception when a validation
588  // error occurs while parsing
589  String lastError;
590  bool startElementRead;
591 
592 private: // static fields
593 };
594 
595 // gcc does not allow full template specialization in class definition
596 // boost::lexical_cast does not understand true and false so implement special version
597 template<>
598 bool XmlReader::TryGetAttributeValue(const char*, bool& value);
599 
600 template<>
601 bool XmlReader::TryGetAttributeValue(const char* attributeName, String& value);
602 
603 template<>
604 bool XmlReader::TryGetAttributeValue(const char* attributeName, Chrono::Duration& value);
605 
606 template<>
607 bool XmlReader::TryGetAttributeValue(const char* attributeName, uint8& value);
608 
609 template<>
610 bool XmlReader::TryGetAttributeValue(const char* attributeName, int8& value);
611 
613 // inline methods of class XmlReader
614 template<typename... Args>
615 inline XmlException XmlReader::CreateException(const char* message, const Args& ... args)
616 {
617  return XmlException::Create(this->filename, this->GetLineNumber(), message, args...);
618 }
619 
621 {
622  return this->filename;
623 }
624 
625 }}}} // namespace Arp::System::Commons::Xml
bool TryGetAttributeValueEnum(const char *attributeName, Enum< T > &value)
trys reads an attribute value from the current element and converts the String to Enum<T> ...
Definition: XmlReader.hpp:356
T ReadElementContent()
reads the content of the current element if a CDATA section is found, the CDATA enclosure is removed ...
Definition: XmlReader.hpp:281
bool MoveToElement()
Moves the position of the current instance to the node that contains the current Attribute node...
XmlException CreateException(const char *message, const Args &... args)
Creates an exception by prefixing the given message with current doc name and line number ...
Definition: XmlReader.hpp:615
This (meta programming) class provides the C++ typename of the as template argument passed type...
Definition: TypeName.hxx:55
bool TryReadEndElement(void)
tries to read until the next end element is reached skips over an empty element
bool Read()
Reads to the next element inside the xml file
bool TryReadStartElement(const char *elementName=nullptr)
trys to read the next element and test if this is a start element with the given name ...
static Enum Parse(const char *input)
Parses the given input string.
Definition: Enum.hxx:190
internal class to handle xml parsing errors in XmlReader
Definition: LibXmlErrorHandler.hpp:15
bool ReadToDescendant(const String &name)
reads the xml file until an element with name is reached or the current element is closed reads to th...
T GetAttributeValueDuration(const char *attributeName)
reads an attribute value from the current element and converts the String to std::chrono::duration i...
Definition: XmlReader.hpp:407
const String & GetDocumentFileName()
Gets the current document filename of this XML reader
Definition: XmlReader.hpp:620
bool TryGetAttributeValue(const char *attributeName, T &value)
tries to read an attribute value from the current element
Definition: XmlReader.hpp:498
static XmlReader Create(const String &filename)
Creates a new Instance of the XmlReader
long GetLineNumber()
Gets the actual line number of this XML reader
bool TryGetAttributeValue(const char *attributeName, T defaultValue, T &value)
tries to read an attribute value from the current element if the attribute was not found or the conve...
Definition: XmlReader.hpp:546
Adapter class for enums to make them loggable and parsable from e.g. XML files.
Definition: Enum.hxx:23
~XmlReader(void)
Destructs this instance and frees all resources.
static XmlException Create(void)
Creates an XmlException instance using a default message text.
Definition: XmlException.hpp:79
static bool TryCreate(const String &filename, XmlReader &reader)
Tries to creates a new Instance of the XmlReader
static XmlReader CreateForString(const String &buffer)
Creates a new Instance of the XmlReader reading from an in memory buffer
Definition: Duration.hpp:17
String ReadElementContent(void)
reads the content of the current element if a CDATA section is found, the CDATA enclosure is removed ...
bool IsEmptyElement()
test if the current element is an empty element
bool SkipElement()
skips the current element with its whole subtree
T GetAttributeValue(const char *attributeName)
reads an attribute value from the current element
Definition: XmlReader.hpp:298
void ReadEndElement(void)
reads until the next end element is reached skips over an empty element
bool Skip()
skips the current element with its whole subtree (use SkipElement() because naming is more clear) ...
std::uint8_t uint8
The Arp unsigned integer type of 1 byte size.
Definition: PrimitiveTypes.hpp:27
Root namespace for the PLCnext API
This exception is used for xml parsing errors.
Definition: XmlException.hpp:14
XmlNodeType GetCurrentNodeType()
gets the type of the current xml element
Class to read an XML File. Non buffered reader, can only read forward
Definition: XmlReader.hpp:24
String GetElementLocalName(void)
Reads the local name of the current active element
bool TryGetAttributeValueDuration(const char *attributeName, T &value)
trys reads an attribute value from the current element and converts the String to std::chrono::durati...
Definition: XmlReader.hpp:436
int GetCurrentDepth()
gets the current depth of the current element inside the xml document
bool ReadToNextSibling(const String &name)
reads to the next element with name which is a sibling of the current element
std::int8_t int8
The Arp integer type of 1 byte size.
Definition: PrimitiveTypes.hpp:29
void ReadStartElement(const char *elementName=nullptr)
reads to the next element and test if this is a start element with the given name ...
bool ReadToFollowing(const String &name)
reads the xml file until an element with name is reached or end of file is reached ...
System components used by the System, Device, Plc or Io domains.
XmlReader(void)
Default contructor.
bool TryGetAttributeValueDuration(const char *attributeName, T defaultValue, T &value)
tries to read an attribute value from the current element as std::crono::duration if the attribute wa...
Definition: XmlReader.hpp:477
Enum< T > GetAttributeValueEnum(const char *attributeName)
reads an attribute value from the current element and converts the String to Enum<T> ...
Definition: XmlReader.hpp:325
bool SkipElementContent()
skips the content of current element with its whole subtree but does not read end element tag ...
bool TryGetAttributeValueEnum(const char *attributeName, T defaultValue, Enum< T > &value)
tries to read an attribute value from the current element as enum if the attribute was not found or t...
Definition: XmlReader.hpp:387