PLCnext API Documentation  22.9.0.33
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);
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();
130 
145  bool Skip(); // TODO: remove this due to unclear naming
152  bool SkipElement();
153 
162 
163  // moves to the element that contains the current attribute
164 
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 
352  template<class T>
353  T GetAttributeValueOfEnum(const char* attributeName)
354  {
355  T result;
356  if (!TryGetAttributeValueEnum<T>(attributeName, result))
357  {
358  throw XmlException(
359  "GetAttributeValueOfEnum failed for mandatory attribute: {0} elementName: {1}", attributeName, GetElementLocalName());
360  }
361  return result;
362  }
363 
378  template<class T>
379  bool TryGetAttributeValueEnum(const char* attributeName, T& value)
380  {
381  char* charValue = GetAttributeValueInternal(attributeName);
382  if (charValue == nullptr)
383  {
384  return false;
385  }
386  std::istringstream iss(charValue);
387  iss >> value;
388  FreeInternalAttributeValue(charValue);
389  return true;
390  }
391 
406  template<class T>
407  bool TryGetAttributeValueEnum(const char* attributeName, Enum<T>& value)
408  {
409  char* charValue = GetAttributeValueInternal(attributeName);
410  if (charValue == nullptr)
411  {
412  return false;
413  }
414 
415  value = Enum<T>::Parse(charValue);
416  FreeInternalAttributeValue(charValue);
417  return true;
418  }
419 
437  template<class T>
438  bool TryGetAttributeValueEnum(const char* attributeName, T defaultValue, Enum<T>& value)
439  {
440  value = defaultValue;
441  return TryGetAttributeValueEnum<T>(attributeName, value);
442  }
443 
444 
457  template<class T>
458  T GetAttributeValueDuration(const char* attributeName)
459  {
460  T returnValue;
461 
462  if (TryGetAttributeValueDuration<T>(attributeName, returnValue))
463  {
464  return returnValue;
465  }
466  else
467  {
468  throw XmlException(
469  "GetAttributeValueDuration failed for mandatory attribute: {0} elementName: {1}",
470  attributeName, GetElementLocalName());
471  }
472  }
486  template<class T>
487  bool TryGetAttributeValueDuration(const char* attributeName, T& value)
488  {
489  char* charValue = GetAttributeValueInternal(attributeName);
490  Chrono::Duration tempDurationValue;
491 
492  if (charValue == nullptr)
493  {
494  return false;
495  }
496 
497  bool result = Chrono::Duration::TryParse(charValue, tempDurationValue);
498  if (!result)
499  {
500  throw XmlException(
501  "TryGetAttributeValueDuration failed for attribute '{0}' in element '{1}': invalid duration format in value '{2}'",
502  attributeName, GetElementLocalName(), charValue);
503  }
504  tempDurationValue.GetDuration(value);
505 
506  FreeInternalAttributeValue(charValue);
507  return result;
508  }
509 
527  template<class T>
528  bool TryGetAttributeValueDuration(const char* attributeName, T defaultValue, T& value)
529  {
530  value = defaultValue;
531  return TryGetAttributeValueDuration<T>(attributeName, value);
532  }
533 
534 
548  template<class T>
549  bool TryGetAttributeValue(const char* attributeName, T& value)
550  {
551  char* charValue = nullptr;
552  try
553  {
554  charValue = GetAttributeValueInternal(attributeName);
555  if (charValue == nullptr)
556  {
557  return false;
558  }
559 
560  // boost::lexical_cast seems fast enough:
561  // http://stackoverflow.com/questions/23582089/is-boostlexical-cast-redundant-with-c11-stoi-stof-and-family
562  // boost::lexical_cast is 10 times slower than atoi on Linux but has more error checks
563  // http://tinodidriksen.com/2010/02/16/cpp-convert-string-to-int-speed/
564  value = boost::lexical_cast<T>(charValue);
565  FreeInternalAttributeValue(charValue);
566  return true;
567  }
568  catch (boost::bad_lexical_cast&)
569  {
570 
571  String strValue(charValue);
572  FreeInternalAttributeValue(charValue);
573  throw XmlException("Cast of attribute {0} to {1} failed value: {2}", attributeName,
574  TypeName<T>(), strValue);
575  }
576  }
577 
578 
596  template<class T>
597  bool TryGetAttributeValue(const char* attributeName, T defaultValue, T& value)
598  {
599 
600  value = defaultValue;
601  return TryGetAttributeValue<T>(attributeName, value);
602  }
603 
604  void EnableValidationException();
605  void DisableValidationException();
606 
616  template<typename... Args>
617  XmlException CreateException(const char* message, const Args& ... args);
618 
619 protected: // operations
620 private: // static methods
621 private: // methods
622  int ReaderState();
623  char* GetAttributeValueInternal(const char* attributeName);
624  void FreeInternalAttributeValue(char* attributeValue);
625  // Move to the content node, if the element is not an content node
626  // content nodes are: non-whitespace text, CDATA, Element, EndElement, EntityReference,
627  // EndEntity
628  XmlNodeType MoveToContent();
629  void EnableValidation(const String& xsdFile);
630  void CheckForValidationException();
631 
632 private: // fields
633  String filename;
634  // internal reader ptr
635  void* reader;
636  bool validationEnabled;
637  LibXmlErrorHandler* errorHandler;
638  bool exceptionOnValidationError; // defines whether to throw an exception when a validation
639  // error occurs while parsing
640  String lastError;
641  bool startElementRead;
642 
643 private: // static fields
644 };
645 
646 // gcc does not allow full template specialization in class definition
647 // boost::lexical_cast does not understand true and false so implement special version
648 template<>
649 bool XmlReader::TryGetAttributeValue(const char*, bool& value);
650 
651 template<>
652 bool XmlReader::TryGetAttributeValue(const char* attributeName, String& value);
653 
654 template<>
655 bool XmlReader::TryGetAttributeValue(const char* attributeName, Chrono::Duration& value);
656 
657 template<>
658 bool XmlReader::TryGetAttributeValue(const char* attributeName, uint8& value);
659 
660 template<>
661 bool XmlReader::TryGetAttributeValue(const char* attributeName, int8& value);
662 
664 // inline methods of class XmlReader
665 template<typename... Args>
666 inline XmlException XmlReader::CreateException(const char* message, const Args& ... args)
667 {
668  return XmlException::Create(this->filename, this->GetLineNumber(), message, args...);
669 }
670 
672 {
673  return this->filename;
674 }
675 
676 }}}} // namespace Arp::System::Commons::Xml
Adapter class for enums to make them loggable and parsable from e.g. XML files.
Definition: Enum.hxx:23
Definition: Duration.hpp:16
This exception is used for xml parsing errors.
Definition: XmlException.hpp:15
static XmlException Create(void)
Creates an XmlException instance using a default message text.
Definition: XmlException.hpp:79
internal class to handle xml parsing errors in XmlReader
Definition: LibXmlErrorHandler.hpp:16
Class to read an XML File. Non buffered reader, can only read forward
Definition: XmlReader.hpp:25
bool TryGetAttributeValueEnum(const char *attributeName, T &value)
trys reads an attribute value from the current element and converts the String to Enum<T>
Definition: XmlReader.hpp:379
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:666
T ReadElementContent()
reads the content of the current element if a CDATA section is found, the CDATA enclosure is removed
Definition: XmlReader.hpp:281
static XmlReader CreateForString(const String &buffer)
Creates a new Instance of the XmlReader reading from an in memory buffer
T GetAttributeValue(const char *attributeName)
reads an attribute value from the current element
Definition: XmlReader.hpp:298
int GetCurrentDepth()
gets the current depth of the current element inside the xml document
bool Read()
Reads to the next element inside the xml file
static bool TryCreate(const String &filename, XmlReader &reader)
Tries to creates a new Instance of the XmlReader
static XmlReader Create(const String &filename, const String &xsdFile)
Creates a new Instance of the XmlReader and enables Schema validation with the XSD schema at xsdFile
XmlNodeType GetCurrentNodeType()
gets the type of the current xml element
bool ReadToNextSibling(const String &name)
reads to the next element with name which is a sibling of the current element
bool IsEmptyElement()
test if the current element is an empty element
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:597
bool SkipElement()
skips the current element with its whole subtree
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 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:528
XmlReader(void)
Default contructor.
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:407
void ReadStartElement(const char *elementName=nullptr)
reads to the next element and test if this is a start element with the given name
XmlReader(XmlReader &&arg)
Move contructor.
static XmlReader Create(const String &filename)
Creates a new Instance of the XmlReader
bool TryReadStartElement(const char *elementName=nullptr)
trys to read the next element and test if this is a start element with the given name
bool MoveToElement()
Moves the position of the current instance to the node that contains the current Attribute node.
T GetAttributeValueOfEnum(const char *attributeName)
reads an attribute value from the current element and converts the String to Enum<T>
Definition: XmlReader.hpp:353
const String & GetDocumentFileName()
Gets the current document filename of this XML reader
Definition: XmlReader.hpp:671
void ReadEndElement(void)
reads until the next end element is reached skips over an empty element
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:458
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...
bool Skip()
skips the current element with its whole subtree (use SkipElement() because naming is more clear)
bool SkipElementContent()
skips the content of current element with its whole subtree but does not read end element tag
String ReadElementContent(void)
reads the content of the current element if a CDATA section is found, the CDATA enclosure is removed
~XmlReader(void)
Destructs this instance and frees all resources.
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:438
bool TryReadStartElement(String &elementName, bool conditioned=true)
trys to read the next element and returns the read element name as out argument
long GetLineNumber()
Gets the actual line number of this XML reader
String GetElementLocalName(void)
Reads the local name of the current active element
bool TryReadEndElement(void)
tries to read until the next end element is reached skips over an empty element
bool ReadToFollowing(const String &name)
reads the xml file until an element with name is reached or end of file is reached
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:487
bool TryGetAttributeValue(const char *attributeName, T &value)
tries to read an attribute value from the current element
Definition: XmlReader.hpp:549
This (meta programming) class provides the C++ typename of the as template argument passed type.
Definition: TypeName.hxx:56
static Enum Parse(const String &input)
Parses the given input string.
Definition: Enum.hxx:228
std::uint8_t uint8
The Arp unsigned integer type of 1 byte size.
Definition: PrimitiveTypes.hpp:27
std::int8_t int8
The Arp integer type of 1 byte size.
Definition: PrimitiveTypes.hpp:29
@ System
System components used by the System, Device, Plc or Io domains.
Root namespace for the PLCnext API