From d16f4c95e3e1db0390ae40b4ad0b3d871ec6a8e3 Mon Sep 17 00:00:00 2001 From: Edouard DUPIN Date: Thu, 25 Feb 2021 01:42:08 +0100 Subject: [PATCH] [DEV] start dev of a new parsing model to support sax and big file in stream --- src/org/atriasoft/exml/Attribute.java | 179 ------ src/org/atriasoft/exml/Comment.java | 87 --- src/org/atriasoft/exml/Declaration.java | 93 --- src/org/atriasoft/exml/DeclarationXML.java | 41 -- src/org/atriasoft/exml/Document.java | 243 -------- src/org/atriasoft/exml/Element.java | 536 ------------------ src/org/atriasoft/exml/Exml.java | 121 ++++ src/org/atriasoft/exml/Text.java | 116 ---- src/org/atriasoft/exml/TextCDATA.java | 29 +- src/org/atriasoft/exml/XmlAttribute.java | 95 ++++ ...tributeList.java => XmlAttributeList.java} | 22 +- src/org/atriasoft/exml/XmlComment.java | 50 ++ src/org/atriasoft/exml/XmlDeclaration.java | 72 +++ src/org/atriasoft/exml/XmlElement.java | 251 ++++++++ .../exml/{Node.java => XmlNode.java} | 38 +- .../exml/{NodeType.java => XmlNodeType.java} | 2 +- src/org/atriasoft/exml/XmlText.java | 51 ++ src/org/atriasoft/exml/builder/Builder.java | 65 +++ .../exml/builder/BuilderGeneric.java | 69 +++ .../exml/exception/ExmlBuilderException.java | 18 + .../exml/{ => internal}/FilePos.java | 2 +- src/org/atriasoft/exml/internal/Tools.java | 61 +- src/org/atriasoft/exml/parser/ParseXml.java | 517 +++++++++++++++++ .../exml/parser/ParsingProperty.java | 87 +++ test/src/test/atriasoft/exml/ExmlLocal.java | 3 +- .../atriasoft/exml/ExmlTestAttribute.java | 38 +- .../test/atriasoft/exml/ExmlTestCData.java | 6 +- .../test/atriasoft/exml/ExmlTestComment.java | 22 +- .../exml/ExmlTestDeclarationXML.java | 4 +- .../test/atriasoft/exml/ExmlTestElement.java | 42 +- 30 files changed, 1546 insertions(+), 1414 deletions(-) delete mode 100644 src/org/atriasoft/exml/Attribute.java delete mode 100644 src/org/atriasoft/exml/Comment.java delete mode 100644 src/org/atriasoft/exml/Declaration.java delete mode 100644 src/org/atriasoft/exml/DeclarationXML.java delete mode 100644 src/org/atriasoft/exml/Document.java delete mode 100644 src/org/atriasoft/exml/Element.java create mode 100644 src/org/atriasoft/exml/Exml.java delete mode 100644 src/org/atriasoft/exml/Text.java create mode 100644 src/org/atriasoft/exml/XmlAttribute.java rename src/org/atriasoft/exml/{AttributeList.java => XmlAttributeList.java} (88%) create mode 100644 src/org/atriasoft/exml/XmlComment.java create mode 100644 src/org/atriasoft/exml/XmlDeclaration.java create mode 100644 src/org/atriasoft/exml/XmlElement.java rename src/org/atriasoft/exml/{Node.java => XmlNode.java} (81%) rename src/org/atriasoft/exml/{NodeType.java => XmlNodeType.java} (94%) create mode 100644 src/org/atriasoft/exml/XmlText.java create mode 100644 src/org/atriasoft/exml/builder/Builder.java create mode 100644 src/org/atriasoft/exml/builder/BuilderGeneric.java create mode 100644 src/org/atriasoft/exml/exception/ExmlBuilderException.java rename src/org/atriasoft/exml/{ => internal}/FilePos.java (98%) create mode 100644 src/org/atriasoft/exml/parser/ParseXml.java create mode 100644 src/org/atriasoft/exml/parser/ParsingProperty.java diff --git a/src/org/atriasoft/exml/Attribute.java b/src/org/atriasoft/exml/Attribute.java deleted file mode 100644 index 851d23f..0000000 --- a/src/org/atriasoft/exml/Attribute.java +++ /dev/null @@ -1,179 +0,0 @@ -/** @file - * @author Edouard DUPIN - * @copyright 2021, Edouard DUPIN, all right reserved - * @license MPL v2.0 (see license file) - */ -package org.atriasoft.exml; - -import org.atriasoft.exml.internal.Log; -import org.atriasoft.exml.internal.PositionParsing; -import org.atriasoft.exml.internal.Tools; - -/** - * Single attribute element - */ -public class Attribute { - - protected FilePos pos; //!< position in the read file (null if the file is not parsed); - protected String value; //!< value of the node (for element this is the name, for text it is the inside text ...); - protected String name; //!< Name of the attribute - - public Attribute() { - this.pos = null; - this.value = ""; - this.name = ""; - } - - public Attribute(final Attribute _obj) { - this.pos = null; - this.value = _obj.value; - this.name = _obj.name; - } - - public Attribute(final String _name) { - this.name = _name; - this.value = ""; - } - - /** - * Constructor - * @param[in] _name Name of the attribute. - * @param[in] _value Value of the attribute. - */ - public Attribute(final String _name, final String _value) { - this.name = _name; - this.value = _value; - } - - public void clear() { - this.value = ""; - } - - @Override - public Attribute clone() { - return new Attribute(this); - } - - /** - * get the current name of the Attribute - * @return String of the attribute - */ - public String getName() { - return this.name; - }; - - /** - * get the current element Value. - * @return the reference of the string value. - */ - public String getValue() { - return this.value; - }; - - protected boolean iGenerate(final StringBuilder _data, final int _indent) { - _data.append(" "); - _data.append(this.name); - _data.append("=\""); - _data.append(this.value); - _data.append("\""); - return true; - }; - - protected boolean iParse(final String _data, final PositionParsing _pos, final boolean _caseSensitive, final FilePos _filePos, final Document _doc) { - Log.verbose("start parse : 'attribute'"); - this.pos = _filePos.clone(); - // search end of the comment : - int lastElementName = _pos.value; - for (int iii = _pos.value; iii < _data.length(); iii++) { - _filePos.check(_data.charAt(iii)); - Tools.drawElementParsed(_data.charAt(iii), _filePos); - if (Tools.checkAvaillable(_data.charAt(iii), false) == true) { - lastElementName = iii; - } else { - break; - } - } - this.name = _data.substring(_pos.value, lastElementName + 1); - if (_caseSensitive == true) { - this.name = this.name.toLowerCase(); - } - // count white space : - final FilePos tmpPos = new FilePos(); - int white = Tools.countWhiteChar(_data, lastElementName + 1, tmpPos); - _filePos.add(tmpPos); - if (lastElementName + white + 1 >= _data.length()) { - _doc.createError(_data, lastElementName + white + 1, _filePos, " parse an xml end with an attribute parsing..."); - return false; - } - if (_data.charAt(lastElementName + white + 1) != '=') { - _doc.createError(_data, lastElementName + white + 1, _filePos, " error attribute parsing == > missing '=' ..."); - return false; - } - white += Tools.countWhiteChar(_data, lastElementName + white + 2, tmpPos); - _filePos.add(tmpPos); - - if (lastElementName + white + 2 >= _data.length()) { - _doc.createError(_data, lastElementName + white + 2, _filePos, " parse an xml end with an attribute parsing..."); - return false; - } - boolean simpleQuoteCase = false; - if (_data.charAt(lastElementName + white + 2) == '\'') { // ' - simpleQuoteCase = true; - } - if (_data.charAt(lastElementName + white + 2) != '"' && _data.charAt(lastElementName + white + 2) != '\'') { // ' - // parse with no element " == > direct value separate with space ... - _filePos.increment(); - int lastAttributePos = lastElementName + white + 2; - for (int iii = lastElementName + white + 2; iii < _data.length(); iii++) { - Tools.drawElementParsed(_data.charAt(iii), _filePos); - if (_filePos.check(_data.charAt(iii)) == true) { - _doc.createError(_data, iii, _filePos, "unexpected '\\n' in an attribute parsing"); - return false; - } - if (_data.charAt(iii) != ' ' && _data.charAt(iii) != '/' && _data.charAt(iii) != '?' && _data.charAt(iii) != '>') { - lastAttributePos = iii + 1; - } else { - break; - } - } - this.value = _data.substring(lastElementName + white + 2, lastAttributePos); - - //EXML_PARSE_ATTRIBUTE(pos << " attribute : " << name << "=\"" << value << "\""); - - _pos.value = lastAttributePos - 1; - return true; - } - int lastAttributePos = lastElementName + white + 3; - for (int iii = lastElementName + white + 3; iii < _data.length(); iii++) { - Tools.drawElementParsed(_data.charAt(iii), _filePos); - _filePos.check(_data.charAt(iii)); - if ((_data.charAt(iii) != '"' && simpleQuoteCase == false) || (_data.charAt(iii) != '\'' && simpleQuoteCase == true)) { // ' - lastAttributePos = iii + 1; - } else { - break; - } - } - this.value = _data.substring(lastElementName + white + 3, lastAttributePos); - - //EXML_PARSE_ATTRIBUTE(pos << " attribute : " << name << "=\"" << value << "\""); - - _pos.value = lastAttributePos; - return true; - } - - /** - * set the name of the attribute - * @param[in] _name New name of the attribute - */ - public void setName(final String _name) { - this.name = _name; - } - - /** - * set the value of the node. - * @param[in] _value New value of the node. - */ - public final void setValue(final String _value) { - this.value = _value; - } -}; \ No newline at end of file diff --git a/src/org/atriasoft/exml/Comment.java b/src/org/atriasoft/exml/Comment.java deleted file mode 100644 index 190b587..0000000 --- a/src/org/atriasoft/exml/Comment.java +++ /dev/null @@ -1,87 +0,0 @@ -/** @file - * @author Edouard DUPIN - * @copyright 2021, Edouard DUPIN, all right reserved - * @license MPL v2.0 (see license file) - */ -package org.atriasoft.exml; - -import org.atriasoft.exml.internal.Log; -import org.atriasoft.exml.internal.PositionParsing; -import org.atriasoft.exml.internal.Tools; - -/** - * Comment node: lt;!-- ... --gt; - */ -public class Comment extends Node { - - public Comment() { - super(); - } - - public Comment(final Comment obj) { - super(obj.value); - } - - /** - * Constructor - * @param[in] _value comment value - */ - public Comment(final String _value) { - super(_value); - } - - @Override - public Comment clone() { - return new Comment(this); - } - - @Override - public NodeType getType() { - return NodeType.COMMENT; - } - - @Override - public boolean iGenerate(final StringBuilder _data, final int _indent) { - Tools.addIndent(_data, _indent); - _data.append("\n"); - return true; - } - - @Override - protected boolean iParse(final String _data, final PositionParsing _pos, final boolean _caseSensitive, final FilePos _filePos, final Document _doc) { - Log.verbose("start parse : 'comment'"); - this.pos = _filePos; - final FilePos tmpPos = new FilePos(); - final int white = Tools.countWhiteChar(_data, _pos.value, tmpPos); - _filePos.add(tmpPos); - // search end of the comment : - for (int iii = _pos.value + white; iii + 2 < _data.length(); iii++) { - Tools.drawElementParsed(_data.charAt(iii), _filePos); - if (_filePos.check(_data.charAt(iii)) == true) { - continue; - } - if (_data.charAt(iii) == '-' && _data.charAt(iii + 1) == '-' && _data.charAt(iii + 2) == '>') { - _filePos.add(2); - // search whitespace : - int newEnd = iii; - for (int jjj = iii - 1; jjj > _pos.value; jjj--) { - if (Tools.isWhiteChar(_data.charAt(jjj)) == true) { - newEnd = jjj; - } else { - break; - } - } - // find end of value: - this.value = _data.substring(_pos.value + white, newEnd); - Log.verbose(" find comment '" + this.value + "'"); - _pos.value = iii + 2; - return true; - } - } - _pos.value = _data.length(); - _doc.createError(_data, _pos.value, _filePos, "comment got end of file without finding end node"); - return false; - } -}; \ No newline at end of file diff --git a/src/org/atriasoft/exml/Declaration.java b/src/org/atriasoft/exml/Declaration.java deleted file mode 100644 index 4d3e625..0000000 --- a/src/org/atriasoft/exml/Declaration.java +++ /dev/null @@ -1,93 +0,0 @@ -/** @file - * @author Edouard DUPIN - * @copyright 2021, Edouard DUPIN, all right reserved - * @license MPL v2.0 (see license file) - */ -package org.atriasoft.exml; - -import org.atriasoft.exml.internal.Log; -import org.atriasoft.exml.internal.PositionParsing; -import org.atriasoft.exml.internal.Tools; - -/** - * Declaration node: lt;?XXXXXX ... gt; - */ -public class Declaration extends AttributeList { - public Declaration() { - super(""); - }; - - public Declaration(final Declaration obj) { - super(obj.value); - for (final Attribute elem : obj.listAttribute) { - this.listAttribute.add(elem.clone()); - } - }; - - /** - * Constructor - * @param[in] _name name of the declaration (xml, xml:xxxx ...) - */ - public Declaration(final String _name) { - super(_name); - } - - @Override - public Declaration clone() { - return new Declaration(this); - } - - @Override - public NodeType getType() { - return NodeType.DECLARATION; - }; - - @Override - protected boolean iGenerate(final StringBuilder _data, final int _indent) { - Tools.addIndent(_data, _indent); - _data.append("\n"); - return true; - } - - @Override - protected boolean iParse(final String _data, final PositionParsing _pos, final boolean _caseSensitive, final FilePos _filePos, final Document _doc) { - Log.verbose("start parse : 'declaration' : '" + this.value + "'"); - this.pos = _filePos; - // search end of the comment : - for (int iii = _pos.value; iii + 1 < _data.length(); iii++) { - Tools.drawElementParsed(_data.charAt(iii), _filePos); - if (_filePos.check(_data.charAt(iii)) == true) { - continue; - } - if (_data.charAt(iii) == '>' || _data.charAt(iii) == '<') { - // an error occured : - _doc.createError(_data, _pos.value, _filePos, " find '>' or '<' instead of '?>'"); - return false; - } - if (_data.charAt(iii) == '?' && _data.charAt(iii + 1) == '>') { - _filePos.increment(); - // find end of declaration: - _pos.value = iii + 1; - return true; - } - if (Tools.checkAvaillable(_data.charAt(iii), true) == true) { - // we find an attibute == > create an element and parse it: - final Attribute attribute = new Attribute(); - _pos.value = iii; - if (attribute.iParse(_data, _pos, _caseSensitive, _filePos, _doc) == false) { - return false; - } - iii = _pos.value; - this.listAttribute.add(attribute); - continue; - } - } - _doc.createError(_data, _pos.value, _filePos, "Text got end of file without finding end node"); - _pos.value = _data.length(); - return false; - } - -} diff --git a/src/org/atriasoft/exml/DeclarationXML.java b/src/org/atriasoft/exml/DeclarationXML.java deleted file mode 100644 index 8dac26c..0000000 --- a/src/org/atriasoft/exml/DeclarationXML.java +++ /dev/null @@ -1,41 +0,0 @@ -/** @file - * @author Edouard DUPIN - * @copyright 2021, Edouard DUPIN, all right reserved - * @license MPL v2.0 (see license file) - */ -package org.atriasoft.exml; - -import org.atriasoft.exml.internal.Log; - -public class DeclarationXML extends Declaration { - public DeclarationXML(final DeclarationXML obj) { - super(obj.value); - for (final Attribute elem : obj.listAttribute) { - this.listAttribute.add(elem.clone()); - } - } - - public DeclarationXML(final String _version, final String _format, final boolean _standalone) { - super("xml"); - if (_version.isEmpty() != true) { - setAttribute("version", _version); - } - if (_format.contentEquals("UTF-8")) { - setAttribute("encoding", "UTF-8"); - } else { - Log.error("Actually does not supported other charset than UTF8"); - setAttribute("encoding", "UTF-8"); - } - if (_standalone == true) { - setAttribute("standalone", "true"); - } else { - setAttribute("standalone", "true"); - } - } - - @Override - public DeclarationXML clone() { - return new DeclarationXML(this); - } - -} diff --git a/src/org/atriasoft/exml/Document.java b/src/org/atriasoft/exml/Document.java deleted file mode 100644 index bfe5e44..0000000 --- a/src/org/atriasoft/exml/Document.java +++ /dev/null @@ -1,243 +0,0 @@ -/** @file - * @author Edouard DUPIN - * @copyright 2021, Edouard DUPIN, all right reserved - * @license MPL v2.0 (see license file) - */ -package org.atriasoft.exml; - -import org.atriasoft.exml.internal.Log; -import org.atriasoft.exml.internal.PositionParsing; -import org.atriasoft.exml.internal.Tools; - -/** @file - * @author Edouard DUPIN - * @copyright 2011, Edouard DUPIN, all right reserved - * @license MPL v2.0 (see license file) - */ -/** - * Basic document element of a document - */ -public class Document extends Element { - - private boolean caseSensitive; //!< check the case sensitive of the nodes and attribute - private boolean writeErrorWhenDetexted; //!< Request print error in parsing just when detected - private String comment; //!< Comment on the error; - private String Line; //!< Parse line error (copy); - private FilePos filePos; //!< position of the error - - /** - * Constructor - */ - public Document() { - this.caseSensitive = false; - this.writeErrorWhenDetexted = true; - this.comment = ""; - this.Line = ""; - this.filePos = new FilePos(0, 0); - } - - @Override - public void clear() { - // TODO Auto-generated method stub - - } - - /** - * Create an error in the parsing (call by the syetm for error management) - * @param[in] _data string of chat is wrong - * @param[in] _pos Position in the file - * @param[in] _filePos human position of the error - * @param[in] _comment Error string to display - */ - public void createError(final String _data, final int _pos, final FilePos _filePos, final String _comment) { - this.comment = _comment; - this.Line = extract_line(_data, _pos); - this.filePos = _filePos; - if (this.writeErrorWhenDetexted == true) { - displayError(); - } - } - - /** - * Display the Document on console - */ - public void display() { - final StringBuilder tmpp = new StringBuilder(); - iGenerate(tmpp, 0); - Log.info("Generated XML : \n" + tmpp.toString()); - } - - /** - * Request display in log of the error - */ - public void displayError() { - if (this.comment.length() == 0) { - Log.error("No error detected ???"); - return; - } - Log.error(this.filePos + " " + this.comment + "\n" + this.Line + "\n" + Tools.createPosPointer(this.Line, this.filePos.getCol())); - //Log.critical("detect error"); - } - - String extract_line(final String data, final int _pos) { - // search back : '\n' - int startPos = data.lastIndexOf('\n', _pos); - if (startPos == _pos) { - startPos = 0; - } else { - startPos++; - } - // search forward : '\n' - int stopPos = _pos; - if (data.length() == _pos) { - stopPos = _pos; - } else if (data.charAt(_pos) != '\n') { - stopPos = data.indexOf('\n', _pos); - if (stopPos == _pos) { - stopPos = data.length(); - } - } - if (startPos == -1) { - startPos = 0; - } else if (startPos >= data.length()) { - return ""; - } - if (stopPos == -1) { - return ""; - } else if (stopPos >= data.length()) { - stopPos = data.length(); - } - return data.substring(startPos, stopPos); - } - - /** - * generate a string that contain the created XML - * @param[out] _data Data where the xml is stored - * @return false : An error occured - * @return true : Parsing is OK - */ - public boolean generate(final StringBuilder _data) { - return iGenerate(_data, 0); - } - /** - * Load the file that might contain the xml - * @param[in] _uri URI of the xml - * @return false : An error occured - * @return true : Parsing is OK - */ - /* - public boolean load( Uri _uri){ - // Start loading the XML : - EXML_VERBOSE("open file (xml) " + _uri); - clear(); - auto fileIo = uri::get(_uri); - if (fileIo == null) { - Log.error("File Does not exist : " + _uri); - return false; - } - if (fileIo->open(io::OpenMode::Read) == false) { - Log.error("Can not open (r) the file : " + _uri); - return false; - } - // load data from the file: - String tmpDataUnicode = fileIo->readAllString(); - // close the file: - fileIo->close(); - // parse the data: - boolean ret = parse(tmpDataUnicode); - //Display(); - return ret; - } - */ - /** - * Store the Xml in the file - * @param[in] _uri URI of the xml - * @return false : An error occured - * @return true : Parsing is OK - */ - - /* - public boolean store( Uri _uri){ - String createData; - if (generate(createData) == false) { - Log.error("Error while creating the XML: " + _uri); - return false; - } - auto fileIo = uri::get(_uri); - if (fileIo == null) { - Log.error("Can not create the uri: " + _uri); - return false; - } - if (fileIo->open(io::OpenMode::Write) == false) { - Log.error("Can not open (r) the file : " + _uri); - return false; - } - fileIo->writeAll(createData); - fileIo->close(); - return true; - } - */ - /** - * get the status of case sensitive mode. - * @return true if case sensitive is active - */ - public boolean getCaseSensitive() { - return this.caseSensitive; - } - - /** - * Get the display of the error status. - * @return true Display error - * @return false Does not display error (get it at end) - */ - public boolean getDisplayError() { - return this.writeErrorWhenDetexted; - } - - @Override - public NodeType getType() { - return NodeType.DOCUMENT; - } - - @Override - public boolean iGenerate(final StringBuilder _data, final int _indent) { - for (int iii = 0; iii < this.listSub.size(); iii++) { - if (this.listSub.get(iii) != null) { - this.listSub.get(iii).iGenerate(_data, _indent); - } - } - return true; - } - - /** - * parse a string that contain an XML - * @param[in] _data Data to parse - * @return false : An error occured - * @return true : Parsing is OK - */ - public boolean parse(final String _data) { - Log.verbose("Start parsing document (type: string) size=" + _data.length()); - clear(); - // came from char == > force in utf8 ... - this.pos = new FilePos(1, 0); - final PositionParsing parsePos = new PositionParsing(); - return subParse(_data, parsePos, this.caseSensitive, this.pos, this, true); - } - - /** - * Enable or diasable the case sensitive (must be done before the call of parsing) - * @param[in] _val true if enable; false else. - */ - // TODO: Naming error, it is insensitive ... - public void setCaseSensitive(final boolean _val) { - this.caseSensitive = _val; - } - - /** - * Set the display of the error when detected. - * @param[in] _value true: display error, false not display error (get it at end) - */ - public void setDisplayError(final boolean _value) { - this.writeErrorWhenDetexted = _value; - } -} diff --git a/src/org/atriasoft/exml/Element.java b/src/org/atriasoft/exml/Element.java deleted file mode 100644 index 1ec0b20..0000000 --- a/src/org/atriasoft/exml/Element.java +++ /dev/null @@ -1,536 +0,0 @@ -/** @file - * @author Edouard DUPIN - * @copyright 2021, Edouard DUPIN, all right reserved - * @license MPL v2.0 (see license file) - */ -package org.atriasoft.exml; - -import java.util.ArrayList; -import java.util.List; -import java.util.ListIterator; - -import org.atriasoft.exml.exception.ExmlNodeDoesNotExist; -import org.atriasoft.exml.internal.Log; -import org.atriasoft.exml.internal.PositionParsing; -import org.atriasoft.exml.internal.Tools; - -/** @file - * @author Edouard DUPIN - * @copyright 2011, Edouard DUPIN, all right reserved - * @license MPL v2.0 (see license file) - */ -/** - * Basic element Node of an XML document lt;YYYYYgt; - */ -public class Element extends AttributeList { - protected List listSub = new ArrayList<>(); //!< List of subNodes; - - /** - * Constructor - */ - public Element() { - super(); - }; - - public Element(final Element obj) throws CloneNotSupportedException { - super(obj.value); - for (final Attribute elem : obj.listAttribute) { - this.listAttribute.add(elem.clone()); - } - for (final Node elem : obj.listSub) { - this.listSub.add(elem.clone()); - } - } - - /** - * Constructor - * @param[in] _value Element name; - */ - public Element(final String _value) { - super(_value); - - }; - - /** - * add a node at the element (not Attribute (move in the attribute automaticly). - * @param[in] _node Pointer of the node to add. - */ - public void append(final Node _node) { - if (_node == null) { - Log.error("Try to set an empty node"); - return; - } - for (int iii = 0; iii < this.listSub.size(); iii++) { - if (this.listSub.get(iii) == _node) { - Log.error("Try to add a node that is already added before !!!"); - return; - } - } - this.listSub.add(_node); - } - - @Override - public void clear() { - super.clear(); - this.listSub.clear(); - }; - - @Override - public Element clone() throws CloneNotSupportedException { - return new Element(this); - } - - /** - * get the Node pointer of the element id. - * @param[in] _id Id of the element. - * @return true if the Node exist. - */ - public boolean existNode(final int _id) { - if (_id < 0 || _id >= this.listSub.size()) { - return false; - } - return true; - } - - /** - * get an element with his name (work only with Element) - * @param[in] _name Name of the element that is requested - * @return true if the Node exist. - */ - public boolean existNode(final String _name) { - if (_name.isEmpty() == true) { - return false; - } - for (int iii = 0; iii < this.listSub.size(); iii++) { - if (this.listSub.get(iii) != null && this.listSub.get(iii).getValue().contentEquals(_name) == true) { - if (this.listSub.get(iii) == null) { - return false; - } - return true; - } - } - return false; - } - - /** - * get the Node pointer of the element id. - * @param[in] _id Id of the element. - * @return Pointer on node. - * @throws ExmlNodeDoesNotExist The Node does not exist - */ - public Node getNode(final int _id) throws ExmlNodeDoesNotExist { - if (_id < 0 || _id >= this.listSub.size()) { - throw new ExmlNodeDoesNotExist("Node does not exist: " + _id + "/" + this.listSub.size()); - } - return this.listSub.get(_id); - } - - /** - * get an element with his name (work only with Element) - * @param[in] _name Name of the element that is requested - * @return Pointer on the node. - * @throws ExmlNodeDoesNotExist The Node does not exist - */ - public Node getNode(final String _name) throws ExmlNodeDoesNotExist { - if (_name.isEmpty() == true) { - throw new ExmlNodeDoesNotExist("Node can not have empty name in " + this.listAttribute.size() + " nodes"); - } - for (int iii = 0; iii < this.listSub.size(); iii++) { - if (this.listSub.get(iii) != null && this.listSub.get(iii).getValue().contentEquals(_name) == true) { - return this.listSub.get(iii); - } - } - throw new ExmlNodeDoesNotExist("Node does not exist: '" + _name + "' in " + this.listAttribute.size()); - } - - /** - * Get the list of the sub-nodes. - * @return List of current nodes. - */ - public List getNodes() { - return this.listSub; - } - - /** - * get the internal data of the element (if the element has some sub node they are converted in xml string == > like this it is not needed to use - * @return the curent data string. if Only one text node, then we get the parssed data (no amp; ...) if more than one node, then we transform ,",',<,> in xml normal text... - */ - public String getText() { - final StringBuilder res = new StringBuilder(); - if (this.listSub.size() == 1) { - if (this.listSub.get(0).getType() == NodeType.TEXT) { - res.append(this.listSub.get(0).getValue()); - } else { - this.listSub.get(0).iGenerate(res, 0); - } - } else { - for (int iii = 0; iii < this.listSub.size(); iii++) { - if (this.listSub.get(iii) != null) { - this.listSub.get(iii).iGenerate(res, 0); - } - } - } - return res.toString(); - } - - @Override - public NodeType getType() { - return NodeType.ELEMENT; - } - - /** - * get the type of the element id. - * @param[in] _id Id of the element. - * @return the Current type of the element or typeUnknow. - * @throws ExmlNodeDoesNotExist The Node does not exist - */ - public NodeType getType(final int _id) throws ExmlNodeDoesNotExist { - if (_id < 0 || _id >= this.listSub.size()) { - throw new ExmlNodeDoesNotExist("Node does not exist: " + _id + "/" + this.listSub.size()); - } - return this.listSub.get(_id).getType(); - } - - @Override - protected boolean iGenerate(final StringBuilder _data, final int _indent) { - Tools.addIndent(_data, _indent); - _data.append("<"); - _data.append(this.value); - super.iGenerate(_data, _indent); - - if (this.listSub.size() > 0) { - if (this.listSub.size() == 1 && this.listSub.get(0) != null && this.listSub.get(0).getType() == NodeType.TEXT && ((Text) this.listSub.get(0)).countLines() == 1) { - _data.append(">"); - this.listSub.get(0).iGenerate(_data, 0); - Log.verbose(" generate : '" + _data + "'"); - } else { - _data.append(">\n"); - for (int iii = 0; iii < this.listSub.size(); iii++) { - if (this.listSub.get(iii) != null) { - this.listSub.get(iii).iGenerate(_data, _indent + 1); - } - } - Tools.addIndent(_data, _indent); - } - _data.append("\n"); - } else { - _data.append("/>\n"); - } - return true; - } - - @Override - protected boolean iParse(final String _data, final PositionParsing _pos, final boolean _caseSensitive, final FilePos _filePos, final Document _doc) { - //EXML_PARSE_ELEMENT("start parse : 'element' named='" + value + "'"); - // note : When start parsing the upper element must have set the value of the element and set the position after this one - this.pos = _filePos.clone(); - // find a normal node ... - for (int iii = _pos.value; iii < _data.length(); iii++) { - _filePos.check(_data.charAt(iii)); - Tools.drawElementParsed(_data.charAt(iii), _filePos); - if (_data.charAt(iii) == '>') { - // we find the end ... - _pos.value = iii + 1; - return subParse(_data, _pos, _caseSensitive, _filePos, _doc, false); - } - if (_data.charAt(iii) == '/') { - // standalone node or error... - if (iii + 1 >= _data.length()) { - _doc.createError(_data, _pos.value, _filePos, "Find end of files ... == > bad case"); - return false; - } - // TODO : Can have white spaces .... - if (_data.charAt(iii + 1) == '>') { - _pos.value = iii + 1; - return true; - } - // error - _doc.createError(_data, _pos.value, _filePos, "Find / without > char ..."); - return false; - } - if (Tools.checkAvaillable(_data.charAt(iii), true) == true) { - // we find an attibute == > create an element and parse it: - final Attribute attribute = new Attribute(); - _pos.value = iii; - if (attribute.iParse(_data, _pos, _caseSensitive, _filePos, _doc) == false) { - return false; - } - iii = _pos.value; - this.listAttribute.add(attribute); - continue; - } - if (Tools.isWhiteChar(_data.charAt(iii)) == false) { - _doc.createError(_data, iii, _filePos, "Find an unknow element : '" + _data.charAt(iii) + "'"); - return false; - } - } - _doc.createError(_data, _pos.value, _filePos, "Unexpecting end of parsing exml::internal::Element : '" + this.value + "' == > check if the '/>' is set or the end of element"); - return false; - } - - /** - * Remove all element with this name - * @param[in] _nodeName Name of nodes to remove. - */ - public void remove(final String _nodeName) { - if (_nodeName == "") { - return; - } - for (final ListIterator iter = this.listSub.listIterator(); iter.hasNext();) { - final Node element = iter.next(); - if (element == null) { - iter.remove(); - continue; - } - if (element.getValue().contentEquals(_nodeName) == true) { - iter.remove(); - } - } - } - - /** - * get the number of sub element in the node (can be Comment ; Element ; Text :Declaration). - * @return a number >=0. - */ - public int size() { - return this.listSub.size(); - } - - /** - * Parse sub node string - * @param[in] _data all file string data - * @param[in,out] _pos Position to start parsing in the file and return the end of parsing - * @param[in] _caseSensitive Case sensitive parsing (usefull for html) - * @param[in] _filePos Current File position of the parsing - * @param[in] _doc Document base reference - * @param[in] _mainNode if true, this is the first root node - * @return true parsing is done OK - * @return false An error appear in the parsing - */ - protected boolean subParse(final String _data, final PositionParsing _pos, final boolean _caseSensitive, final FilePos _filePos, final Document _doc) { - return subParse(_data, _pos, _caseSensitive, _filePos, _doc, false); - } - - protected boolean subParse(final String _data, final PositionParsing _pos, final boolean _caseSensitive, final FilePos _filePos, final Document _doc, final boolean _mainNode) { - //EXML_PARSE_ELEMENT(" start subParse ... " << _pos << " " << _filePos); - for (int iii = _pos.value; iii < _data.length(); iii++) { - _filePos.check(_data.charAt(iii)); - Tools.drawElementParsed(_data.charAt(iii), _filePos); - final FilePos tmpPos = new FilePos(); - if (_data.charAt(iii) == '<') { - final int white = Tools.countWhiteChar(_data, iii + 1, tmpPos); - if (iii + white + 1 >= _data.length()) { - _filePos.add(tmpPos); - _doc.createError(_data, _pos.value, _filePos, "End file with '<' char == > invalide XML"); - _pos.value = iii + white; - return false; - } - // Detect type of the element: - if (_data.charAt(iii + white + 1) == '>') { - _filePos.add(tmpPos); - _doc.createError(_data, _pos.value, _filePos, "Find '>' with no element in the element..."); - _pos.value = iii + white + 1; - return false; - } - if (_data.charAt(iii + white + 1) == '?') { - tmpPos.increment(); - // TODO : white space ... - if (Tools.checkAvaillable(_data.charAt(iii + white + 2), true) == false) { - _doc.createError(_data, _pos.value, _filePos, "Find unavaillable name in the Declaration node..."); - _pos.value = iii + white + 1; - return false; - } - //EXML_DEBUG("Generate node name : '" << _data[iii+1] << "'"); - int endPosName = iii + white + 1; - // generate element name ... - for (int jjj = iii + white + 2; jjj < _data.length(); jjj++) { - if (Tools.checkAvaillable(_data.charAt(jjj), false) == true) { - // we find the end ... - endPosName = jjj; - } else { - break; - } - tmpPos.check(_data.charAt(jjj)); - } - String tmpname = _data.substring(iii + white + 2, endPosName + 1); - if (_caseSensitive == true) { - tmpname = tmpname.toLowerCase(); - } - // Find declaration marker - final Declaration declaration = new Declaration(tmpname); - _filePos.add(tmpPos); - _pos.value = endPosName + 1; - if (declaration.iParse(_data, _pos, _caseSensitive, _filePos, _doc) == false) { - return false; - } - iii = _pos.value; - this.listSub.add(declaration); - continue; - } - if (_data.charAt(iii + white + 1) == '!') { - tmpPos.increment(); - // Find special block element - if (iii + white + 2 >= _data.length()) { - _doc.createError(_data, _pos.value, _filePos, "End file with ' invalide XML"); - return false; - } - if (_data.charAt(iii + white + 2) == '-') { - tmpPos.increment(); - if (iii + white + 3 >= _data.length()) { - _doc.createError(_data, _pos.value, _filePos, "End file with ' invalide XML"); - return false; - } - if (_data.charAt(iii + white + 3) != '-') { - _doc.createError(_data, _pos.value, _filePos, "Element parse with ' invalide XML"); - return false; - } - tmpPos.increment(); - // find comment: - final Comment comment = new Comment(); - _pos.value = iii + white + 4; - _filePos.add(tmpPos); - if (comment.iParse(_data, _pos, _caseSensitive, _filePos, _doc) == false) { - return false; - } - iii = _pos.value; - this.listSub.add(comment); - } else if (_data.charAt(iii + white + 2) == '[') { - tmpPos.increment(); - if (iii + white + 8 >= _data.length()) { - _doc.createError(_data, _pos.value, _filePos, "End file with ' invalide XML"); - return false; - } - if (_data.charAt(iii + white + 3) != 'C' || _data.charAt(iii + white + 4) != 'D' || _data.charAt(iii + white + 5) != 'A' || _data.charAt(iii + white + 6) != 'T' - || _data.charAt(iii + white + 7) != 'A' || _data.charAt(iii + white + 8) != '[') { - _doc.createError(_data, _pos.value, _filePos, "Element parse with ' invalide XML"); - return false; - } - tmpPos.add(6); - // find text: - final TextCDATA text = new TextCDATA(); - _pos.value = iii + 9 + white; - _filePos.add(tmpPos); - if (text.iParse(_data, _pos, _caseSensitive, _filePos, _doc) == false) { - return false; - } - iii = _pos.value; - this.listSub.add(text); - } else { - _doc.createError(_data, _pos.value, _filePos, "End file with ' invalide XML"); - return false; - } - continue; - } - if (_data.charAt(iii + white + 1) == '/') { - tmpPos.increment(); - //EXML_DEBUG("Generate node name : '" << _data[iii+1] << "'"); - int endPosName = iii + white + 1; - // generate element name ... - for (int jjj = iii + white + 2; jjj < _data.length(); jjj++) { - if (Tools.checkAvaillable(_data.charAt(jjj), false) == true) { - // we find the end ... - endPosName = jjj; - } else { - break; - } - tmpPos.check(_data.charAt(jjj)); - } - String tmpname = _data.substring(iii + white + 2, endPosName + 1); - if (_caseSensitive == true) { - tmpname = tmpname.toLowerCase(); - } - if (tmpname.contentEquals(this.value) == true) { - // find end of node : - // find > element ... - for (int jjj = endPosName + 1; jjj < _data.length(); jjj++) { - Tools.drawElementParsed(_data.charAt(jjj), _filePos); - if (tmpPos.check(_data.charAt(jjj)) == true) { - continue; - } - if (_data.charAt(jjj) == '>') { - _pos.value = jjj; - _filePos.add(tmpPos); - return true; - } else if (_data.charAt(jjj) != '\r' && _data.charAt(jjj) != ' ' && _data.charAt(jjj) != '\t') { - _filePos.add(tmpPos); - _doc.createError(_data, jjj, _filePos, "End node error : have data inside end node other than [ \\n\\t\\r] " + this.value + "'"); - return false; - } - } - } else { - _doc.createError(_data, _pos.value, _filePos, "End node error : '" + tmpname + "' != '" + this.value + "'"); - return false; - } - } - if (_data.charAt(iii + white + 1) == '>') { - // end of something == > this is really bad - _doc.createError(_data, _pos.value, _filePos, "Find '>' chars == > invalide XML"); - return false; - } - - if (Tools.checkAvaillable(_data.charAt(iii + white + 1), true) == true) { - tmpPos.increment(); - Log.debug("Generate node name : '" + _data.charAt(iii + 1) + "'"); - int endPosName = iii + white + 1; - // generate element name ... - for (int jjj = iii + white + 2; jjj < _data.length(); jjj++) { - if (Tools.checkAvaillable(_data.charAt(jjj), false) == true) { - // we find the end ... - endPosName = jjj; - } else { - break; - } - tmpPos.check(_data.charAt(jjj)); - } - String tmpname = _data.substring(iii + white + 1, endPosName + 1); - if (_caseSensitive == true) { - tmpname = tmpname.toLowerCase(); - } - Log.debug("find node named : '" + tmpname + "'"); - // find text: - final Element element = new Element(tmpname); - _pos.value = endPosName + 1; - _filePos.add(tmpPos); - if (element.iParse(_data, _pos, _caseSensitive, _filePos, _doc) == false) { - return false; - } - iii = _pos.value; - this.listSub.add(element); - continue; - } - _filePos.add(tmpPos); - // here we have an error : - _doc.createError(_data, _pos.value, _filePos, "Find an ununderstanding element : '" + _data.charAt(iii + white + 1) + "'"); - return false; - } else { - if (_data.charAt(iii) == '>') { - _doc.createError(_data, _pos.value, _filePos, "Find elemement '>' == > no reason to be here ..."); - return false; - } - // might to be data text ... - if (_data.charAt(iii) == '\n' || _data.charAt(iii) == ' ' || _data.charAt(iii) == '\t' || _data.charAt(iii) == '\r') { - // empty spaces == > nothing to do .... - } else { - // find data == > parse it... - final Text text = new Text(); - _pos.value = iii; - _filePos.add(tmpPos); - if (text.iParse(_data, _pos, _caseSensitive, _filePos, _doc) == false) { - return false; - } - iii = _pos.value; - this.listSub.add(text); - } - } - } - if (_mainNode == true) { - return true; - } - _doc.createError(_data, _pos.value, _filePos, "Did not find end of the exml::internal::Element : '" + this.value + "'"); - return false; - } - -}; \ No newline at end of file diff --git a/src/org/atriasoft/exml/Exml.java b/src/org/atriasoft/exml/Exml.java new file mode 100644 index 0000000..fd84368 --- /dev/null +++ b/src/org/atriasoft/exml/Exml.java @@ -0,0 +1,121 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2021, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ + +package org.atriasoft.exml; + +import org.atriasoft.exml.builder.Builder; +import org.atriasoft.exml.builder.BuilderGeneric; +import org.atriasoft.exml.exception.ExmlBuilderException; +import org.atriasoft.exml.internal.Log; +import org.atriasoft.exml.parser.ParseXml; + +public class Exml { + /** + * Display the Document on console + */ + public static void display() { + final StringBuilder tmpp = new StringBuilder(); + //iGenerate(tmpp, 0); + Log.info("Generated XML : \n" + tmpp.toString()); + } + + /** + * generate a string that contain the created XML + * @param[out] _data Data where the xml is stored + * @return false : An error occured + * @return true : Parsing is OK + */ + public static boolean generate(final StringBuilder _data) { + //return iGenerate(_data, 0); + return false; + } + + public static boolean iGenerate(final StringBuilder _data, final int _indent) { + /* + for (int iii = 0; iii < this.listSub.size(); iii++) { + if (this.listSub.get(iii) != null) { + this.listSub.get(iii).iGenerate(_data, _indent); + } + } + */ + return true; + } + + public static XmlNode parse(final String data) { + + final Builder builder = new BuilderGeneric(); + final ParseXml parser = new ParseXml(builder); + + Object out = null; + try { + out = parser.parse(data); + } catch (final ExmlBuilderException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return (XmlNode) out; + } + + /** + * Load the file that might contain the xml + * @param[in] _uri URI of the xml + * @return false : An error occured + * @return true : Parsing is OK + */ + /* + public boolean load( Uri _uri){ + // Start loading the XML : + EXML_VERBOSE("open file (xml) " + _uri); + clear(); + auto fileIo = uri::get(_uri); + if (fileIo == null) { + Log.error("File Does not exist : " + _uri); + return false; + } + if (fileIo->open(io::OpenMode::Read) == false) { + Log.error("Can not open (r) the file : " + _uri); + return false; + } + // load data from the file: + String tmpDataUnicode = fileIo->readAllString(); + // close the file: + fileIo->close(); + // parse the data: + boolean ret = parse(tmpDataUnicode); + //Display(); + return ret; + } + */ + /** + * Store the Xml in the file + * @param[in] _uri URI of the xml + * @return false : An error occured + * @return true : Parsing is OK + */ + + /* + public boolean store( Uri _uri){ + String createData; + if (generate(createData) == false) { + Log.error("Error while creating the XML: " + _uri); + return false; + } + auto fileIo = uri::get(_uri); + if (fileIo == null) { + Log.error("Can not create the uri: " + _uri); + return false; + } + if (fileIo->open(io::OpenMode::Write) == false) { + Log.error("Can not open (r) the file : " + _uri); + return false; + } + fileIo->writeAll(createData); + fileIo->close(); + return true; + } + */ + private Exml() {} +} diff --git a/src/org/atriasoft/exml/Text.java b/src/org/atriasoft/exml/Text.java deleted file mode 100644 index cc643a3..0000000 --- a/src/org/atriasoft/exml/Text.java +++ /dev/null @@ -1,116 +0,0 @@ -/** @file - * @author Edouard DUPIN - * @copyright 2021, Edouard DUPIN, all right reserved - * @license MPL v2.0 (see license file) - */ -package org.atriasoft.exml; - -import org.atriasoft.exml.internal.Log; -import org.atriasoft.exml.internal.PositionParsing; -import org.atriasoft.exml.internal.Tools; - -/** - * Text node interface (internal data between two Marker: <XXX> ALL here </XXX> - */ -public class Text extends Node { - - // transform the Text with : - // "<" == "<" - // ">" == ">" - // "&" == "&" - // "'" == "'" - // """ == """ - private static String replaceSpecialChar(final String _inval) { - final String out = _inval; - out.replace("<", "<"); - out.replace(">", ">"); - out.replace("'", "'"); - out.replace(""", "\""); - out.replace("&", "&"); - //EXML_ERROR("INNN '"<< _inval << "' => '" << out << "'"); - return out; - } - - private static String replaceSpecialCharOut(final String _inval) { - final String out = _inval; - out.replace("<", "<"); - out.replace(">", ">"); - out.replace("'", "'"); - out.replace("\"", """); - out.replace("&", "&"); - //EXML_ERROR("OUTTT '"<< _inval << "' => '" << out << "'"); - return out; - } - - /** - * Constructor - */ - public Text() {}; - - /** - * Constructor - * @param[in] _data String data of the current Text - */ - public Text(final String _data) { - super(_data); - } - - /** - * count the number of line in the current text - * @return The number of lines - */ - protected int countLines() { - int count = 1; - for (int iii = 0; iii < this.value.length(); iii++) { - if (this.value.charAt(iii) == '\n') { - count++; - } - } - return count; - } - - @Override - public NodeType getType() { - return NodeType.TEXT; - }; - - @Override - protected boolean iGenerate(final StringBuilder _data, final int _indent) { - _data.append(replaceSpecialCharOut(this.value)); - return true; - } - - @Override - protected boolean iParse(final String _data, final PositionParsing _pos, final boolean _caseSensitive, final FilePos _filePos, final Document _doc) { - Log.verbose("start parse : 'text'"); - this.pos = _filePos.clone(); - // search end of the comment : - for (int iii = _pos.value; iii < _data.length(); iii++) { - Tools.drawElementParsed(_data.charAt(iii), _filePos); - if (_filePos.check(_data.charAt(iii)) == true) { - continue; - } - if (_data.charAt(iii) == '>' || _data.charAt(iii) == '<') { - // search whitespace : - int newEnd = iii; - for (int jjj = iii - 1; jjj > _pos.value; --jjj) { - if (Tools.isWhiteChar(_data.charAt(jjj)) == true) { - newEnd = jjj; - } else { - break; - } - } - // find end of value: - this.value = _data.substring(_pos.value, newEnd); - Log.verbose(" find text '" + this.value + "'"); - _pos.value = iii - 1; - this.value = replaceSpecialChar(this.value); - return true; - } - } - _doc.createError(_data, _pos.value, _filePos, "Text got end of file without finding end node"); - _pos.value = _data.length(); - return false; - } - -}; \ No newline at end of file diff --git a/src/org/atriasoft/exml/TextCDATA.java b/src/org/atriasoft/exml/TextCDATA.java index d06e0ea..832c735 100644 --- a/src/org/atriasoft/exml/TextCDATA.java +++ b/src/org/atriasoft/exml/TextCDATA.java @@ -5,11 +5,7 @@ */ package org.atriasoft.exml; -import org.atriasoft.exml.internal.Log; -import org.atriasoft.exml.internal.PositionParsing; -import org.atriasoft.exml.internal.Tools; - -public class TextCDATA extends Text { +public class TextCDATA extends XmlText { public TextCDATA() { super(); } @@ -26,27 +22,4 @@ public class TextCDATA extends Text { return true; } - @Override - protected boolean iParse(final String _data, final PositionParsing _pos, final boolean _caseSensitive, final FilePos _filePos, final Document _doc) { - Log.verbose("start parse : 'text::CDATA'"); - this.pos = _filePos.clone(); - // search end of the comment : - for (int iii = _pos.value; iii + 2 < _data.length(); iii++) { - Tools.drawElementParsed(_data.charAt(iii), _filePos); - if (_filePos.check(_data.charAt(iii)) == true) { - continue; - } - if (_data.charAt(iii) == ']' && _data.charAt(iii + 1) == ']' && _data.charAt(iii + 2) == '>') { - // find end of value: - _filePos.add(2); - this.value = _data.substring(_pos.value, iii); - Log.verbose(" find text CDATA '" + this.value + "'"); - _pos.value = iii + 2; - return true; - } - } - _doc.createError(_data, _pos.value, _filePos, "text CDATA got end of file without finding end node"); - _pos.value = _data.length(); - return false; - } } diff --git a/src/org/atriasoft/exml/XmlAttribute.java b/src/org/atriasoft/exml/XmlAttribute.java new file mode 100644 index 0000000..c6f891e --- /dev/null +++ b/src/org/atriasoft/exml/XmlAttribute.java @@ -0,0 +1,95 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2021, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +package org.atriasoft.exml; + +import org.atriasoft.exml.internal.FilePos; + +/** + * Single attribute element + */ +public class XmlAttribute { + + protected FilePos pos; //!< position in the read file (null if the file is not parsed); + protected String value; //!< value of the node (for element this is the name, for text it is the inside text ...); + protected String name; //!< Name of the attribute + + public XmlAttribute() { + this.pos = null; + this.value = ""; + this.name = ""; + } + + public XmlAttribute(final String _name) { + this.name = _name; + this.value = ""; + } + + /** + * Constructor + * @param[in] _name Name of the attribute. + * @param[in] _value Value of the attribute. + */ + public XmlAttribute(final String _name, final String _value) { + this.name = _name; + this.value = _value; + } + + public XmlAttribute(final XmlAttribute _obj) { + this.pos = null; + this.value = _obj.value; + this.name = _obj.name; + } + + public void clear() { + this.value = ""; + } + + @Override + public XmlAttribute clone() { + return new XmlAttribute(this.name, this.value); + } + + /** + * get the current name of the Attribute + * @return String of the attribute + */ + public String getName() { + return this.name; + }; + + /** + * get the current element Value. + * @return the reference of the string value. + */ + public String getValue() { + return this.value; + }; + + protected boolean iGenerate(final StringBuilder _data, final int _indent) { + _data.append(" "); + _data.append(this.name); + _data.append("=\""); + _data.append(this.value); + _data.append("\""); + return true; + }; + + /** + * set the name of the attribute + * @param[in] _name New name of the attribute + */ + public void setName(final String _name) { + this.name = _name; + } + + /** + * set the value of the node. + * @param[in] _value New value of the node. + */ + public final void setValue(final String _value) { + this.value = _value; + } +}; \ No newline at end of file diff --git a/src/org/atriasoft/exml/AttributeList.java b/src/org/atriasoft/exml/XmlAttributeList.java similarity index 88% rename from src/org/atriasoft/exml/AttributeList.java rename to src/org/atriasoft/exml/XmlAttributeList.java index 2ab3fd8..74797fd 100644 --- a/src/org/atriasoft/exml/AttributeList.java +++ b/src/org/atriasoft/exml/XmlAttributeList.java @@ -16,10 +16,10 @@ import org.atriasoft.exml.internal.Log; /** * List of all attribute element in a node */ -public abstract class AttributeList extends Node { - protected List listAttribute = new ArrayList<>(); //!< list of all attribute; +public abstract class XmlAttributeList extends XmlNode { + protected List listAttribute = new ArrayList<>(); //!< list of all attribute; - public AttributeList() { + public XmlAttributeList() { super(); }; @@ -27,7 +27,7 @@ public abstract class AttributeList extends Node { * Constructor * @param[in] _value Node value; */ - public AttributeList(final String _value) { + public XmlAttributeList(final String _value) { super(_value); } @@ -35,7 +35,7 @@ public abstract class AttributeList extends Node { * Add attribute on the List * @param[in] _attr Pointer on the attribute */ - public void appendAttribute(final Attribute _attr) { + public void appendAttribute(final XmlAttribute _attr) { if (_attr == null) { Log.error("Try to set an empty node"); return; @@ -82,7 +82,7 @@ public abstract class AttributeList extends Node { * @return Pointer on the attribute or NULL * @throws ExmlAttributeDoesNotExist The attribute does not exist. */ - public Attribute getAttr(final int _id) throws ExmlAttributeDoesNotExist { + public XmlAttribute getAttr(final int _id) throws ExmlAttributeDoesNotExist { if (_id < 0 || _id >= this.listAttribute.size()) { throw new ExmlAttributeDoesNotExist("Attribute does not exist: " + _id + "/" + this.listAttribute.size()); } @@ -107,7 +107,7 @@ public abstract class AttributeList extends Node { throw new ExmlAttributeDoesNotExist("Attribute does not exist: " + _name + " in " + this.listAttribute.size() + " attributes"); } - public List getAttributes() { + public List getAttributes() { return this.listAttribute; } @@ -118,7 +118,7 @@ public abstract class AttributeList extends Node { * @throws ExmlAttributeDoesNotExist The attribute does not exist. */ public Pair getAttrPair(final int _id) throws ExmlAttributeDoesNotExist { - final Attribute att = getAttr(_id); + final XmlAttribute att = getAttr(_id); return new Pair(att.getName(), att.getValue()); } @@ -142,8 +142,8 @@ public abstract class AttributeList extends Node { if (_name.length() == 0) { return false; } - for (final ListIterator iter = this.listAttribute.listIterator(); iter.hasNext();) { - final Attribute element = iter.next(); + for (final ListIterator iter = this.listAttribute.listIterator(); iter.hasNext();) { + final XmlAttribute element = iter.next(); if (element == null) { iter.remove(); continue; @@ -170,7 +170,7 @@ public abstract class AttributeList extends Node { return; } } - final Attribute attr = new Attribute(_name, _value); + final XmlAttribute attr = new XmlAttribute(_name, _value); this.listAttribute.add(attr); } diff --git a/src/org/atriasoft/exml/XmlComment.java b/src/org/atriasoft/exml/XmlComment.java new file mode 100644 index 0000000..de47de1 --- /dev/null +++ b/src/org/atriasoft/exml/XmlComment.java @@ -0,0 +1,50 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2021, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +package org.atriasoft.exml; + +import org.atriasoft.exml.internal.Tools; + +/** + * Comment node: lt;!-- ... --gt; + */ +public class XmlComment extends XmlNode { + + public XmlComment() { + super(); + } + + /** + * Constructor + * @param[in] _value comment value + */ + public XmlComment(final String _value) { + super(_value); + } + + public XmlComment(final XmlComment obj) { + super(obj.value); + } + + @Override + public XmlComment clone() { + return new XmlComment(this); + } + + @Override + public XmlNodeType getType() { + return XmlNodeType.COMMENT; + } + + @Override + public boolean iGenerate(final StringBuilder _data, final int _indent) { + Tools.addIndent(_data, _indent); + _data.append("\n"); + return true; + } + +}; \ No newline at end of file diff --git a/src/org/atriasoft/exml/XmlDeclaration.java b/src/org/atriasoft/exml/XmlDeclaration.java new file mode 100644 index 0000000..bdb56f1 --- /dev/null +++ b/src/org/atriasoft/exml/XmlDeclaration.java @@ -0,0 +1,72 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2021, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +package org.atriasoft.exml; + +import org.atriasoft.exml.internal.Log; +import org.atriasoft.exml.internal.Tools; + +/** + * Declaration node: lt;?XXXXXX ... gt; + */ +public class XmlDeclaration extends XmlAttributeList { + public XmlDeclaration() { + super(""); + }; + + /** + * Constructor + * @param[in] _name name of the declaration (xml, xml:xxxx ...) + */ + public XmlDeclaration(final String _name) { + super(_name); + } + + public XmlDeclaration(final String _version, final String _format, final boolean _standalone) { + super("xml"); + if (_version.isEmpty() != true) { + setAttribute("version", _version); + } + if (_format.contentEquals("UTF-8")) { + setAttribute("encoding", "UTF-8"); + } else { + Log.error("Actually does not supported other charset than UTF8"); + setAttribute("encoding", "UTF-8"); + } + if (_standalone == true) { + setAttribute("standalone", "true"); + } else { + setAttribute("standalone", "true"); + } + }; + + public XmlDeclaration(final XmlDeclaration obj) { + super(obj.value); + for (final XmlAttribute elem : obj.listAttribute) { + this.listAttribute.add(elem.clone()); + } + } + + @Override + public XmlDeclaration clone() { + return new XmlDeclaration(this); + } + + @Override + public XmlNodeType getType() { + return XmlNodeType.DECLARATION; + }; + + @Override + protected boolean iGenerate(final StringBuilder _data, final int _indent) { + Tools.addIndent(_data, _indent); + _data.append("\n"); + return true; + } + +} diff --git a/src/org/atriasoft/exml/XmlElement.java b/src/org/atriasoft/exml/XmlElement.java new file mode 100644 index 0000000..63e4e8b --- /dev/null +++ b/src/org/atriasoft/exml/XmlElement.java @@ -0,0 +1,251 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2021, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +package org.atriasoft.exml; + +import java.util.ArrayList; +import java.util.List; +import java.util.ListIterator; + +import org.atriasoft.exml.exception.ExmlNodeDoesNotExist; +import org.atriasoft.exml.internal.Log; +import org.atriasoft.exml.internal.Tools; + +/** @file + * @author Edouard DUPIN + * @copyright 2011, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +/** + * Basic element Node of an XML document lt;YYYYYgt; + */ +public class XmlElement extends XmlAttributeList { + protected List listSub = new ArrayList<>(); //!< List of subNodes; + + /** + * Constructor + */ + public XmlElement() { + super(); + }; + + /** + * Constructor + * @param[in] _value Element name; + */ + public XmlElement(final String _value) { + super(_value); + + } + + public XmlElement(final XmlElement obj) throws CloneNotSupportedException { + super(obj.value); + for (final XmlAttribute elem : obj.listAttribute) { + this.listAttribute.add(elem.clone()); + } + for (final XmlNode elem : obj.listSub) { + this.listSub.add(elem.clone()); + } + }; + + /** + * add a node at the element (not Attribute (move in the attribute automaticly). + * @param[in] _node Pointer of the node to add. + */ + public void append(final XmlNode _node) { + if (_node == null) { + Log.error("Try to set an empty node"); + return; + } + for (int iii = 0; iii < this.listSub.size(); iii++) { + if (this.listSub.get(iii) == _node) { + Log.error("Try to add a node that is already added before !!!"); + return; + } + } + this.listSub.add(_node); + } + + @Override + public void clear() { + super.clear(); + this.listSub.clear(); + }; + + @Override + public XmlElement clone() throws CloneNotSupportedException { + return new XmlElement(this); + } + + /** + * get the Node pointer of the element id. + * @param[in] _id Id of the element. + * @return true if the Node exist. + */ + public boolean existNode(final int _id) { + if (_id < 0 || _id >= this.listSub.size()) { + return false; + } + return true; + } + + /** + * get an element with his name (work only with Element) + * @param[in] _name Name of the element that is requested + * @return true if the Node exist. + */ + public boolean existNode(final String _name) { + if (_name.isEmpty() == true) { + return false; + } + for (int iii = 0; iii < this.listSub.size(); iii++) { + if (this.listSub.get(iii) != null && this.listSub.get(iii).getValue().contentEquals(_name) == true) { + if (this.listSub.get(iii) == null) { + return false; + } + return true; + } + } + return false; + } + + /** + * get the Node pointer of the element id. + * @param[in] _id Id of the element. + * @return Pointer on node. + * @throws ExmlNodeDoesNotExist The Node does not exist + */ + public XmlNode getNode(final int _id) throws ExmlNodeDoesNotExist { + if (_id < 0 || _id >= this.listSub.size()) { + throw new ExmlNodeDoesNotExist("Node does not exist: " + _id + "/" + this.listSub.size()); + } + return this.listSub.get(_id); + } + + /** + * get an element with his name (work only with Element) + * @param[in] _name Name of the element that is requested + * @return Pointer on the node. + * @throws ExmlNodeDoesNotExist The Node does not exist + */ + public XmlNode getNode(final String _name) throws ExmlNodeDoesNotExist { + if (_name.isEmpty() == true) { + throw new ExmlNodeDoesNotExist("Node can not have empty name in " + this.listAttribute.size() + " nodes"); + } + for (int iii = 0; iii < this.listSub.size(); iii++) { + if (this.listSub.get(iii) != null && this.listSub.get(iii).getValue().contentEquals(_name) == true) { + return this.listSub.get(iii); + } + } + throw new ExmlNodeDoesNotExist("Node does not exist: '" + _name + "' in " + this.listAttribute.size()); + } + + /** + * Get the list of the sub-nodes. + * @return List of current nodes. + */ + public List getNodes() { + return this.listSub; + } + + /** + * get the internal data of the element (if the element has some sub node they are converted in xml string == > like this it is not needed to use + * @return the curent data string. if Only one text node, then we get the parssed data (no amp; ...) if more than one node, then we transform ,",',<,> in xml normal text... + */ + public String getText() { + final StringBuilder res = new StringBuilder(); + if (this.listSub.size() == 1) { + if (this.listSub.get(0).getType() == XmlNodeType.TEXT) { + res.append(this.listSub.get(0).getValue()); + } else { + this.listSub.get(0).iGenerate(res, 0); + } + } else { + for (int iii = 0; iii < this.listSub.size(); iii++) { + if (this.listSub.get(iii) != null) { + this.listSub.get(iii).iGenerate(res, 0); + } + } + } + return res.toString(); + } + + @Override + public XmlNodeType getType() { + return XmlNodeType.ELEMENT; + } + + /** + * get the type of the element id. + * @param[in] _id Id of the element. + * @return the Current type of the element or typeUnknow. + * @throws ExmlNodeDoesNotExist The Node does not exist + */ + public XmlNodeType getType(final int _id) throws ExmlNodeDoesNotExist { + if (_id < 0 || _id >= this.listSub.size()) { + throw new ExmlNodeDoesNotExist("Node does not exist: " + _id + "/" + this.listSub.size()); + } + return this.listSub.get(_id).getType(); + } + + @Override + protected boolean iGenerate(final StringBuilder _data, final int _indent) { + Tools.addIndent(_data, _indent); + _data.append("<"); + _data.append(this.value); + super.iGenerate(_data, _indent); + + if (this.listSub.size() > 0) { + if (this.listSub.size() == 1 && this.listSub.get(0) != null && this.listSub.get(0).getType() == XmlNodeType.TEXT && ((XmlText) this.listSub.get(0)).countLines() == 1) { + _data.append(">"); + this.listSub.get(0).iGenerate(_data, 0); + Log.verbose(" generate : '" + _data + "'"); + } else { + _data.append(">\n"); + for (int iii = 0; iii < this.listSub.size(); iii++) { + if (this.listSub.get(iii) != null) { + this.listSub.get(iii).iGenerate(_data, _indent + 1); + } + } + Tools.addIndent(_data, _indent); + } + _data.append("\n"); + } else { + _data.append("/>\n"); + } + return true; + } + + /** + * Remove all element with this name + * @param[in] _nodeName Name of nodes to remove. + */ + public void remove(final String _nodeName) { + if (_nodeName == "") { + return; + } + for (final ListIterator iter = this.listSub.listIterator(); iter.hasNext();) { + final XmlNode element = iter.next(); + if (element == null) { + iter.remove(); + continue; + } + if (element.getValue().contentEquals(_nodeName) == true) { + iter.remove(); + } + } + } + + /** + * get the number of sub element in the node (can be Comment ; Element ; Text :Declaration). + * @return a number >=0. + */ + public int size() { + return this.listSub.size(); + } + +}; \ No newline at end of file diff --git a/src/org/atriasoft/exml/Node.java b/src/org/atriasoft/exml/XmlNode.java similarity index 81% rename from src/org/atriasoft/exml/Node.java rename to src/org/atriasoft/exml/XmlNode.java index 8025db3..420bcd3 100644 --- a/src/org/atriasoft/exml/Node.java +++ b/src/org/atriasoft/exml/XmlNode.java @@ -5,12 +5,12 @@ */ package org.atriasoft.exml; -import org.atriasoft.exml.internal.PositionParsing; +import org.atriasoft.exml.internal.FilePos; /** * Basic main object of all xml elements. */ -public abstract class Node { +public abstract class XmlNode { protected FilePos pos; //!< position in the read file (null if the file is not parsed) protected String value; //!< value of the node (for element this is the name, for text it is the inside text ...); @@ -18,7 +18,7 @@ public abstract class Node { /** * basic element of a xml structure */ - public Node() { + public XmlNode() { this.pos = null; } @@ -26,7 +26,7 @@ public abstract class Node { * basic element of a xml structure * @param[in] _value value of the node */ - public Node(final String _value) { + public XmlNode(final String _value) { this.pos = null; this.value = _value; } @@ -40,7 +40,7 @@ public abstract class Node { } @Override - protected Node clone() throws CloneNotSupportedException { + protected XmlNode clone() throws CloneNotSupportedException { throw new CloneNotSupportedException("Can not clone an abstract class ..."); } @@ -56,7 +56,7 @@ public abstract class Node { * get the node type. * @return the type of the Node. */ - public abstract NodeType getType(); + public abstract XmlNodeType getType(); /** * get the current element Value. @@ -83,14 +83,14 @@ public abstract class Node { * @param[in,out] _doc Base document reference * @return false if an error occured. */ - protected abstract boolean iParse(String _data, PositionParsing _pos, boolean _caseSensitive, FilePos _filePos, Document _doc); + //protected abstract boolean iParse(String _data, PositionParsing _pos, boolean _caseSensitive, FilePos _filePos, Document _doc); /** * check if the node is a Comment * @return true if the node is a Comment */ public final boolean isComment() { - return this instanceof Comment; + return this instanceof XmlComment; } /** @@ -98,7 +98,7 @@ public abstract class Node { * @return true if the node is a Declaration */ public final boolean isDeclaration() { - return this instanceof Declaration; + return this instanceof XmlDeclaration; } /** @@ -114,7 +114,7 @@ public abstract class Node { * @return true if the node is a Element */ public final boolean isElement() { - return this instanceof Element; + return this instanceof XmlElement; } /** @@ -122,7 +122,7 @@ public abstract class Node { * @return true if the node is a Text */ public final boolean isText() { - return this instanceof Text; + return this instanceof XmlText; } /** @@ -138,16 +138,16 @@ public abstract class Node { * Cast the element in a Comment if it is possible. * @return pointer on the class or null. */ - public final Comment toComment() { - return (Comment) this; + public final XmlComment toComment() { + return (XmlComment) this; } /** * Cast the element in a Declaration if it is possible. * @return pointer on the class or null. */ - public final Declaration toDeclaration() { - return (Declaration) this; + public final XmlDeclaration toDeclaration() { + return (XmlDeclaration) this; } /** @@ -162,16 +162,16 @@ public abstract class Node { * Cast the element in a Element if it is possible. * @return pointer on the class or null. */ - public final Element toElement() { - return (Element) this; + public final XmlElement toElement() { + return (XmlElement) this; } /** * Cast the element in a Text if it is possible. * @return pointer on the class or null. */ - public final Text toText() { - return (Text) this; + public final XmlText toText() { + return (XmlText) this; } } diff --git a/src/org/atriasoft/exml/NodeType.java b/src/org/atriasoft/exml/XmlNodeType.java similarity index 94% rename from src/org/atriasoft/exml/NodeType.java rename to src/org/atriasoft/exml/XmlNodeType.java index 427fa79..27f1ed3 100644 --- a/src/org/atriasoft/exml/NodeType.java +++ b/src/org/atriasoft/exml/XmlNodeType.java @@ -8,7 +8,7 @@ package org.atriasoft.exml; /** * Type of the XML elements. */ -public enum NodeType { +public enum XmlNodeType { DOCUMENT, //!< all the file main access DECLARATION, //!< <?xml ... ?> ELEMENT, //!< the <XXX> ... </XXX> diff --git a/src/org/atriasoft/exml/XmlText.java b/src/org/atriasoft/exml/XmlText.java new file mode 100644 index 0000000..b4205da --- /dev/null +++ b/src/org/atriasoft/exml/XmlText.java @@ -0,0 +1,51 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2021, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +package org.atriasoft.exml; + +/** + * Text node interface (internal data between two Marker: <XXX> ALL here </XXX> + */ +public class XmlText extends XmlNode { + + /** + * Constructor + */ + public XmlText() {}; + + /** + * Constructor + * @param[in] _data String data of the current Text + */ + public XmlText(final String _data) { + super(_data); + } + + /** + * count the number of line in the current text + * @return The number of lines + */ + protected int countLines() { + int count = 1; + for (int iii = 0; iii < this.value.length(); iii++) { + if (this.value.charAt(iii) == '\n') { + count++; + } + } + return count; + } + + @Override + public XmlNodeType getType() { + return XmlNodeType.TEXT; + }; + + @Override + protected boolean iGenerate(final StringBuilder _data, final int _indent) { + _data.append(replaceSpecialCharOut(this.value)); + return true; + } + +}; \ No newline at end of file diff --git a/src/org/atriasoft/exml/builder/Builder.java b/src/org/atriasoft/exml/builder/Builder.java new file mode 100644 index 0000000..c82f3ae --- /dev/null +++ b/src/org/atriasoft/exml/builder/Builder.java @@ -0,0 +1,65 @@ +package org.atriasoft.exml.builder; + +import org.atriasoft.exml.exception.ExmlBuilderException; + +/** + * @author heero + * + */ +/** + * @author heero + * + */ +public interface Builder { + + /** + * New comment added on this Element + * @param element Element representing the node of the Comment is added + * @param comment Comment value + * @throws ExmlBuilderException Error with this node or element. + */ + void newComment(Object element, String comment) throws ExmlBuilderException; + + /** + * New comment added on this Element + * @param parent Element representing the node of the Declaration is added + * @param text Name of the declaration + * @return Declaration object value + * @throws ExmlBuilderException Error with this node or element. + */ + Object newDeclaration(Object parent, String text) throws ExmlBuilderException; + + /** + * Add a new sub-element on the current parent element + * @param parent Element representing the node of the Element is added + * @param nodeName New element name. + * @return the object representing the Element. + * @throws ExmlBuilderException Error with this node or element. + */ + Object newElement(Object parent, String nodeName) throws ExmlBuilderException; + + /** + * Add a property on the Element. + * @param element Element representing the node of the property is added + * @param propertyName Name of the property + * @param propertyValue Value of the property + * @throws ExmlBuilderException Error with this node or element. + */ + void newProperty(Object element, String propertyName, String propertyValue) throws ExmlBuilderException; + + /** + * Create or get the root element of the document + * @return An object that id a root element. + * @throws ExmlBuilderException Error with this node or element. + */ + Object newRoot() throws ExmlBuilderException; + + /** + * Add a text value on the current Element + * @param parent Parent element where the Text is added + * @param text Test to add. + * @throws ExmlBuilderException Error with this node or element. + */ + void newText(Object parent, String text) throws ExmlBuilderException; + +} diff --git a/src/org/atriasoft/exml/builder/BuilderGeneric.java b/src/org/atriasoft/exml/builder/BuilderGeneric.java new file mode 100644 index 0000000..46c901b --- /dev/null +++ b/src/org/atriasoft/exml/builder/BuilderGeneric.java @@ -0,0 +1,69 @@ +package org.atriasoft.exml.builder; + +import org.atriasoft.exml.XmlAttribute; +import org.atriasoft.exml.XmlAttributeList; +import org.atriasoft.exml.XmlComment; +import org.atriasoft.exml.XmlDeclaration; +import org.atriasoft.exml.XmlElement; +import org.atriasoft.exml.XmlText; +import org.atriasoft.exml.exception.ExmlBuilderException; + +public class BuilderGeneric implements Builder { + + @Override + public void newComment(final Object element, final String comment) throws ExmlBuilderException { + if (element instanceof XmlElement) { + final XmlElement elem = (XmlElement) element; + elem.append(new XmlComment(comment)); + } + throw new ExmlBuilderException("can not add Comment on something else than Element"); + } + + @Override + public Object newDeclaration(final Object parent, final String text) throws ExmlBuilderException { + if (parent instanceof XmlElement) { + final XmlElement elem = (XmlElement) parent; + final XmlDeclaration dec = new XmlDeclaration(text); + elem.append(dec); + return dec; + } + throw new ExmlBuilderException("can not add Declaration on something else than Element"); + } + + @Override + public Object newElement(final Object parent, final String nodeName) throws ExmlBuilderException { + if (parent instanceof XmlElement) { + final XmlElement elem = (XmlElement) parent; + final XmlElement eee = new XmlElement(nodeName); + elem.append(eee); + return eee; + } + throw new ExmlBuilderException("can not add Element on something else than Element"); + } + + @Override + public void newProperty(final Object element, final String propertyName, final String propertyValue) throws ExmlBuilderException { + if (element instanceof XmlAttributeList) { + final XmlAttributeList attr = (XmlAttributeList) element; + attr.appendAttribute(new XmlAttribute(propertyName, propertyValue)); + return; + } + throw new ExmlBuilderException("can not add Attribute on something else than Element or Declaration"); + } + + @Override + public Object newRoot() throws ExmlBuilderException { + return new XmlElement(); + } + + @Override + public void newText(final Object parent, final String text) throws ExmlBuilderException { + if (parent instanceof XmlElement) { + final XmlElement attr = (XmlElement) parent; + attr.append(new XmlText(text)); + return; + } + throw new ExmlBuilderException("can not add Text on something else than Element or Declaration"); + } + +} diff --git a/src/org/atriasoft/exml/exception/ExmlBuilderException.java b/src/org/atriasoft/exml/exception/ExmlBuilderException.java new file mode 100644 index 0000000..5fbedca --- /dev/null +++ b/src/org/atriasoft/exml/exception/ExmlBuilderException.java @@ -0,0 +1,18 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2021, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +package org.atriasoft.exml.exception; + +public class ExmlBuilderException extends ExmlException { + /** + * Generate Unique ID for serialization + */ + private static final long serialVersionUID = 1L; + + public ExmlBuilderException(final String data) { + super(data); + } + +} diff --git a/src/org/atriasoft/exml/FilePos.java b/src/org/atriasoft/exml/internal/FilePos.java similarity index 98% rename from src/org/atriasoft/exml/FilePos.java rename to src/org/atriasoft/exml/internal/FilePos.java index eb1b2b8..ccd1fa2 100644 --- a/src/org/atriasoft/exml/FilePos.java +++ b/src/org/atriasoft/exml/internal/FilePos.java @@ -3,7 +3,7 @@ * @copyright 2021, Edouard DUPIN, all right reserved * @license MPL v2.0 (see license file) */ -package org.atriasoft.exml; +package org.atriasoft.exml.internal; /** @file * @author Edouard DUPIN diff --git a/src/org/atriasoft/exml/internal/Tools.java b/src/org/atriasoft/exml/internal/Tools.java index 93a9c4c..f85c7d5 100644 --- a/src/org/atriasoft/exml/internal/Tools.java +++ b/src/org/atriasoft/exml/internal/Tools.java @@ -1,7 +1,5 @@ package org.atriasoft.exml.internal; -import org.atriasoft.exml.FilePos; - public class Tools { /** * add indentation of the string input. @@ -90,6 +88,37 @@ public class Tools { } } + public static String extract_line(final String data, final int _pos) { + // search back : '\n' + int startPos = data.lastIndexOf('\n', _pos); + if (startPos == _pos) { + startPos = 0; + } else { + startPos++; + } + // search forward : '\n' + int stopPos = _pos; + if (data.length() == _pos) { + stopPos = _pos; + } else if (data.charAt(_pos) != '\n') { + stopPos = data.indexOf('\n', _pos); + if (stopPos == _pos) { + stopPos = data.length(); + } + } + if (startPos == -1) { + startPos = 0; + } else if (startPos >= data.length()) { + return ""; + } + if (stopPos == -1) { + return ""; + } else if (stopPos >= data.length()) { + stopPos = data.length(); + } + return data.substring(startPos, stopPos); + } + public static boolean isWhiteChar(final Character _val) { if (_val == ' ' || _val == '\t' || _val == '\n' || _val == '\r') { return true; @@ -97,5 +126,33 @@ public class Tools { return false; } + // transform the Text with : + // "<" == "<" + // ">" == ">" + // "&" == "&" + // "'" == "'" + // """ == """ + public static String replaceSpecialChar(final String _inval) { + final String out = _inval; + out.replace("<", "<"); + out.replace(">", ">"); + out.replace("'", "'"); + out.replace(""", "\""); + out.replace("&", "&"); + //EXML_ERROR("INNN '"<< _inval << "' => '" << out << "'"); + return out; + } + + public static String replaceSpecialCharOut(final String _inval) { + final String out = _inval; + out.replace("<", "<"); + out.replace(">", ">"); + out.replace("'", "'"); + out.replace("\"", """); + out.replace("&", "&"); + //EXML_ERROR("OUTTT '"<< _inval << "' => '" << out << "'"); + return out; + } + private Tools() {} } diff --git a/src/org/atriasoft/exml/parser/ParseXml.java b/src/org/atriasoft/exml/parser/ParseXml.java new file mode 100644 index 0000000..1037ac5 --- /dev/null +++ b/src/org/atriasoft/exml/parser/ParseXml.java @@ -0,0 +1,517 @@ +package org.atriasoft.exml.parser; + +import org.atriasoft.exml.builder.Builder; +import org.atriasoft.exml.exception.ExmlBuilderException; +import org.atriasoft.exml.internal.FilePos; +import org.atriasoft.exml.internal.Log; +import org.atriasoft.exml.internal.PositionParsing; +import org.atriasoft.exml.internal.Tools; + +public class ParseXml { + // global builder that is generate the final Tree + private final Builder builder; + + public ParseXml(final Builder builder) { + this.builder = builder; + } + + protected boolean iParseAttribute(final Object parent, final String _data, final PositionParsing _pos, final FilePos _filePos, final ParsingProperty parsingProperty) throws ExmlBuilderException { + Log.verbose("start parse : 'attribute'"); + final FilePos pos = _filePos.clone(); + // search end of the comment : + int lastElementName = _pos.value; + for (int iii = _pos.value; iii < _data.length(); iii++) { + _filePos.check(_data.charAt(iii)); + Tools.drawElementParsed(_data.charAt(iii), _filePos); + if (Tools.checkAvaillable(_data.charAt(iii), false) == true) { + lastElementName = iii; + } else { + break; + } + } + String name = _data.substring(_pos.value, lastElementName + 1); + String value = ""; + if (parsingProperty.getCaseSensitive() == true) { + name = name.toLowerCase(); + } + // count white space : + final FilePos tmpPos = new FilePos(); + int white = Tools.countWhiteChar(_data, lastElementName + 1, tmpPos); + _filePos.add(tmpPos); + if (lastElementName + white + 1 >= _data.length()) { + parsingProperty.createError(_data, lastElementName + white + 1, _filePos, " parse an xml end with an attribute parsing..."); + return false; + } + if (_data.charAt(lastElementName + white + 1) != '=') { + parsingProperty.createError(_data, lastElementName + white + 1, _filePos, " error attribute parsing == > missing '=' ..."); + return false; + } + white += Tools.countWhiteChar(_data, lastElementName + white + 2, tmpPos); + _filePos.add(tmpPos); + + if (lastElementName + white + 2 >= _data.length()) { + parsingProperty.createError(_data, lastElementName + white + 2, _filePos, " parse an xml end with an attribute parsing..."); + return false; + } + boolean simpleQuoteCase = false; + if (_data.charAt(lastElementName + white + 2) == '\'') { // ' + simpleQuoteCase = true; + } + if (_data.charAt(lastElementName + white + 2) != '"' && _data.charAt(lastElementName + white + 2) != '\'') { // ' + // parse with no element " == > direct value separate with space ... + _filePos.increment(); + int lastAttributePos = lastElementName + white + 2; + for (int iii = lastElementName + white + 2; iii < _data.length(); iii++) { + Tools.drawElementParsed(_data.charAt(iii), _filePos); + if (_filePos.check(_data.charAt(iii)) == true) { + parsingProperty.createError(_data, iii, _filePos, "unexpected '\\n' in an attribute parsing"); + return false; + } + if (_data.charAt(iii) != ' ' && _data.charAt(iii) != '/' && _data.charAt(iii) != '?' && _data.charAt(iii) != '>') { + lastAttributePos = iii + 1; + } else { + break; + } + } + value = _data.substring(lastElementName + white + 2, lastAttributePos); + + //EXML_PARSE_ATTRIBUTE(pos << " attribute : " << name << "=\"" << value << "\""); + + _pos.value = lastAttributePos - 1; + this.builder.newProperty(parent, name, value); + return true; + } + int lastAttributePos = lastElementName + white + 3; + for (int iii = lastElementName + white + 3; iii < _data.length(); iii++) { + Tools.drawElementParsed(_data.charAt(iii), _filePos); + _filePos.check(_data.charAt(iii)); + if ((_data.charAt(iii) != '"' && simpleQuoteCase == false) || (_data.charAt(iii) != '\'' && simpleQuoteCase == true)) { // ' + lastAttributePos = iii + 1; + } else { + break; + } + } + value = _data.substring(lastElementName + white + 3, lastAttributePos); + + //EXML_PARSE_ATTRIBUTE(pos << " attribute : " << name << "=\"" << value << "\""); + + _pos.value = lastAttributePos; + this.builder.newProperty(parent, name, value); + return true; + } + + protected boolean iParseCDATA(final Object parent, final String _data, final PositionParsing _pos, final FilePos _filePos, final ParsingProperty parsingProperty) throws ExmlBuilderException { + Log.verbose("start parse : 'text::CDATA'"); + final FilePos pos = _filePos.clone(); + // search end of the comment : + for (int iii = _pos.value; iii + 2 < _data.length(); iii++) { + Tools.drawElementParsed(_data.charAt(iii), _filePos); + if (_filePos.check(_data.charAt(iii)) == true) { + continue; + } + if (_data.charAt(iii) == ']' && _data.charAt(iii + 1) == ']' && _data.charAt(iii + 2) == '>') { + // find end of value: + _filePos.add(2); + final String valueCData = _data.substring(_pos.value, iii); + Log.verbose(" find text CDATA '" + valueCData + "'"); + _pos.value = iii + 2; + this.builder.newText(parent, valueCData); + return true; + } + } + parsingProperty.createError(_data, _pos.value, _filePos, "text CDATA got end of file without finding end node"); + _pos.value = _data.length(); + return false; + } + + protected boolean iParseComment(final Object parent, final String _data, final PositionParsing _pos, final FilePos _filePos, final ParsingProperty parsingProperty) throws ExmlBuilderException { + Log.verbose("start parse : 'comment'"); + final FilePos pos = _filePos; + final FilePos tmpPos = new FilePos(); + final int white = Tools.countWhiteChar(_data, _pos.value, tmpPos); + _filePos.add(tmpPos); + // search end of the comment : + for (int iii = _pos.value + white; iii + 2 < _data.length(); iii++) { + Tools.drawElementParsed(_data.charAt(iii), _filePos); + if (_filePos.check(_data.charAt(iii)) == true) { + continue; + } + if (_data.charAt(iii) == '-' && _data.charAt(iii + 1) == '-' && _data.charAt(iii + 2) == '>') { + _filePos.add(2); + // search whitespace : + int newEnd = iii; + for (int jjj = iii - 1; jjj > _pos.value; jjj--) { + if (Tools.isWhiteChar(_data.charAt(jjj)) == true) { + newEnd = jjj; + } else { + break; + } + } + // find end of value: + final String value2 = _data.substring(_pos.value + white, newEnd); + Log.verbose(" find comment '" + value2 + "'"); + this.builder.newComment(parent, value2); + _pos.value = iii + 2; + return true; + } + } + _pos.value = _data.length(); + parsingProperty.createError(_data, _pos.value, _filePos, "comment got end of file without finding end node"); + return false; + } + + protected boolean iParseDeclaration(final Object parent, final String _data, final PositionParsing _pos, final FilePos _filePos, final ParsingProperty parsingProperty) + throws ExmlBuilderException { + final FilePos pos = _filePos; + // search end of the comment : + for (int iii = _pos.value; iii + 1 < _data.length(); iii++) { + Tools.drawElementParsed(_data.charAt(iii), _filePos); + if (_filePos.check(_data.charAt(iii)) == true) { + continue; + } + if (_data.charAt(iii) == '>' || _data.charAt(iii) == '<') { + // an error occured : + parsingProperty.createError(_data, _pos.value, _filePos, " find '>' or '<' instead of '?>'"); + return false; + } + if (_data.charAt(iii) == '?' && _data.charAt(iii + 1) == '>') { + _filePos.increment(); + // find end of declaration: + _pos.value = iii + 1; + return true; + } + if (Tools.checkAvaillable(_data.charAt(iii), true) == true) { + // we find an attibute ==> parse it: + _pos.value = iii; + if (iParseAttribute(parent, _data, _pos, _filePos, parsingProperty) == false) { + return false; + } + iii = _pos.value; + continue; + } + } + parsingProperty.createError(_data, _pos.value, _filePos, "Text got end of file without finding end node"); + _pos.value = _data.length(); + return false; + } + + protected boolean iParseElement(final Object parent, final String nameElement, final String _data, final PositionParsing _pos, final FilePos _filePos, final ParsingProperty parsingProperty) + throws ExmlBuilderException { + // note : When start parsing the upper element must have set the value of the element and set the position after this one + final FilePos pos = _filePos.clone(); + // find a normal node ... + for (int iii = _pos.value; iii < _data.length(); iii++) { + _filePos.check(_data.charAt(iii)); + Tools.drawElementParsed(_data.charAt(iii), _filePos); + if (_data.charAt(iii) == '>') { + // we find the end ... + _pos.value = iii + 1; + return subParseElement(parent, nameElement, _data, _pos, _filePos, parsingProperty); + } + if (_data.charAt(iii) == '/') { + // standalone node or error... + if (iii + 1 >= _data.length()) { + parsingProperty.createError(_data, _pos.value, _filePos, "Find end of files ... == > bad case"); + return false; + } + // TODO : Can have white spaces .... + if (_data.charAt(iii + 1) == '>') { + _pos.value = iii + 1; + return true; + } + // error + parsingProperty.createError(_data, _pos.value, _filePos, "Find / without > char ..."); + return false; + } + if (Tools.checkAvaillable(_data.charAt(iii), true) == true) { + // we find an attibute ==> parse it: + _pos.value = iii; + if (iParseAttribute(parent, _data, _pos, _filePos, parsingProperty) == false) { + return false; + } + iii = _pos.value; + continue; + } + if (Tools.isWhiteChar(_data.charAt(iii)) == false) { + parsingProperty.createError(_data, iii, _filePos, "Find an unknow element : '" + _data.charAt(iii) + "'"); + return false; + } + } + parsingProperty.createError(_data, _pos.value, _filePos, "Unexpecting end of parsing exml::internal::Element : '" + nameElement + "' == > check if the '/>' is set or the end of element"); + return false; + } + + protected boolean iParseText(final Object parent, final String _data, final PositionParsing _pos, final FilePos _filePos, final ParsingProperty parsingProperty) throws ExmlBuilderException { + Log.verbose("start parse : 'text'"); + final FilePos pos = _filePos.clone(); + // search end of the comment : + for (int iii = _pos.value; iii < _data.length(); iii++) { + Tools.drawElementParsed(_data.charAt(iii), _filePos); + if (_filePos.check(_data.charAt(iii)) == true) { + continue; + } + if (_data.charAt(iii) == '>' || _data.charAt(iii) == '<') { + // search whitespace : + int newEnd = iii; + for (int jjj = iii - 1; jjj > _pos.value; --jjj) { + if (Tools.isWhiteChar(_data.charAt(jjj)) == true) { + newEnd = jjj; + } else { + break; + } + } + // find end of value: + String valueText = _data.substring(_pos.value, newEnd); + Log.verbose("find text '" + valueText + "'"); + _pos.value = iii - 1; + valueText = Tools.replaceSpecialChar(valueText); + this.builder.newText(parent, valueText); + return true; + } + } + parsingProperty.createError(_data, _pos.value, _filePos, "Text got end of file without finding end node"); + _pos.value = _data.length(); + return false; + } + + public Object parse(final String data) throws ExmlBuilderException { + + Log.verbose("Start parsing document (type: string) size=" + data.length()); + // came from char == > force in utf8 ... + final FilePos pos = new FilePos(1, 0); + final PositionParsing parsePos = new PositionParsing(); + final ParsingProperty property = new ParsingProperty(); + final Object rootNode = this.builder.newRoot(); + if (subParseElement(rootNode, null, data, parsePos, pos, property) == true) { + return rootNode; + } + return null; + + } + + /** + * Parse sub node string + * @param[in] _data all file string data + * @param[in,out] _pos Position to start parsing in the file and return the end of parsing + * @param[in] _caseSensitive Case sensitive parsing (usefull for html) + * @param[in] _filePos Current File position of the parsing + * @param[in] _doc Document base reference + * @param[in] _mainNode if true, this is the first root node + * @return true parsing is done OK + * @return false An error appear in the parsing + */ + protected boolean subParseElement(final Object parent, final String nameElement, final String _data, final PositionParsing _pos, final FilePos _filePos, final ParsingProperty parsingProperty) + throws ExmlBuilderException { + //EXML_PARSE_ELEMENT(" start subParse ... " << _pos << " " << _filePos); + for (int iii = _pos.value; iii < _data.length(); iii++) { + _filePos.check(_data.charAt(iii)); + Tools.drawElementParsed(_data.charAt(iii), _filePos); + final FilePos tmpPos = new FilePos(); + if (_data.charAt(iii) == '<') { + final int white = Tools.countWhiteChar(_data, iii + 1, tmpPos); + if (iii + white + 1 >= _data.length()) { + _filePos.add(tmpPos); + parsingProperty.createError(_data, _pos.value, _filePos, "End file with '<' char == > invalide XML"); + _pos.value = iii + white; + return false; + } + // Detect type of the element: + if (_data.charAt(iii + white + 1) == '>') { + _filePos.add(tmpPos); + parsingProperty.createError(_data, _pos.value, _filePos, "Find '>' with no element in the element..."); + _pos.value = iii + white + 1; + return false; + } + if (_data.charAt(iii + white + 1) == '?') { + tmpPos.increment(); + // TODO : white space ... + if (Tools.checkAvaillable(_data.charAt(iii + white + 2), true) == false) { + parsingProperty.createError(_data, _pos.value, _filePos, "Find unavaillable name in the Declaration node..."); + _pos.value = iii + white + 1; + return false; + } + //EXML_DEBUG("Generate node name : '" << _data[iii+1] << "'"); + int endPosName = iii + white + 1; + // generate element name ... + for (int jjj = iii + white + 2; jjj < _data.length(); jjj++) { + if (Tools.checkAvaillable(_data.charAt(jjj), false) == true) { + // we find the end ... + endPosName = jjj; + } else { + break; + } + tmpPos.check(_data.charAt(jjj)); + } + String tmpname = _data.substring(iii + white + 2, endPosName + 1); + if (parsingProperty.getCaseSensitive() == true) { + tmpname = tmpname.toLowerCase(); + } + // Find declaration marker + final Object declaration = this.builder.newDeclaration(parent, tmpname); + _filePos.add(tmpPos); + _pos.value = endPosName + 1; + Log.verbose("start parse : 'declaration' : '" + tmpname + "'"); + if (iParseDeclaration(declaration, _data, _pos, _filePos, parsingProperty) == false) { + return false; + } + iii = _pos.value; + continue; + } + if (_data.charAt(iii + white + 1) == '!') { + tmpPos.increment(); + // Find special block element + if (iii + white + 2 >= _data.length()) { + parsingProperty.createError(_data, _pos.value, _filePos, "End file with ' invalide XML"); + return false; + } + if (_data.charAt(iii + white + 2) == '-') { + tmpPos.increment(); + if (iii + white + 3 >= _data.length()) { + parsingProperty.createError(_data, _pos.value, _filePos, "End file with ' invalide XML"); + return false; + } + if (_data.charAt(iii + white + 3) != '-') { + parsingProperty.createError(_data, _pos.value, _filePos, "Element parse with ' invalide XML"); + return false; + } + tmpPos.increment(); + // find comment: + _pos.value = iii + white + 4; + _filePos.add(tmpPos); + if (iParseComment(parent, _data, _pos, _filePos, parsingProperty) == false) { + return false; + } + iii = _pos.value; + } else if (_data.charAt(iii + white + 2) == '[') { + tmpPos.increment(); + if (iii + white + 8 >= _data.length()) { + parsingProperty.createError(_data, _pos.value, _filePos, "End file with ' invalide XML"); + return false; + } + if (_data.charAt(iii + white + 3) != 'C' || _data.charAt(iii + white + 4) != 'D' || _data.charAt(iii + white + 5) != 'A' || _data.charAt(iii + white + 6) != 'T' + || _data.charAt(iii + white + 7) != 'A' || _data.charAt(iii + white + 8) != '[') { + parsingProperty.createError(_data, _pos.value, _filePos, "Element parse with ' invalide XML"); + return false; + } + tmpPos.add(6); + // find text: + _pos.value = iii + 9 + white; + _filePos.add(tmpPos); + if (iParseCDATA(parent, _data, _pos, _filePos, parsingProperty) == false) { + return false; + } + iii = _pos.value; + } else { + parsingProperty.createError(_data, _pos.value, _filePos, "End file with ' invalide XML"); + return false; + } + continue; + } + if (_data.charAt(iii + white + 1) == '/') { + tmpPos.increment(); + //EXML_DEBUG("Generate node name : '" << _data[iii+1] << "'"); + int endPosName = iii + white + 1; + // generate element name ... + for (int jjj = iii + white + 2; jjj < _data.length(); jjj++) { + if (Tools.checkAvaillable(_data.charAt(jjj), false) == true) { + // we find the end ... + endPosName = jjj; + } else { + break; + } + tmpPos.check(_data.charAt(jjj)); + } + String tmpname = _data.substring(iii + white + 2, endPosName + 1); + if (parsingProperty.getCaseSensitive() == true) { + tmpname = tmpname.toLowerCase(); + } + if (tmpname.contentEquals(nameElement) == true) { + // find end of node : + // find > element ... + for (int jjj = endPosName + 1; jjj < _data.length(); jjj++) { + Tools.drawElementParsed(_data.charAt(jjj), _filePos); + if (tmpPos.check(_data.charAt(jjj)) == true) { + continue; + } + if (_data.charAt(jjj) == '>') { + _pos.value = jjj; + _filePos.add(tmpPos); + return true; + } else if (_data.charAt(jjj) != '\r' && _data.charAt(jjj) != ' ' && _data.charAt(jjj) != '\t') { + _filePos.add(tmpPos); + parsingProperty.createError(_data, jjj, _filePos, "End node error : have data inside end node other than [ \\n\\t\\r] " + nameElement + "'"); + return false; + } + } + } else { + parsingProperty.createError(_data, _pos.value, _filePos, "End node error : '" + tmpname + "' != '" + nameElement + "'"); + return false; + } + } + if (_data.charAt(iii + white + 1) == '>') { + // end of something == > this is really bad + parsingProperty.createError(_data, _pos.value, _filePos, "Find '>' chars == > invalide XML"); + return false; + } + + if (Tools.checkAvaillable(_data.charAt(iii + white + 1), true) == true) { + tmpPos.increment(); + Log.debug("Generate node name : '" + _data.charAt(iii + 1) + "'"); + int endPosName = iii + white + 1; + // generate element name ... + for (int jjj = iii + white + 2; jjj < _data.length(); jjj++) { + if (Tools.checkAvaillable(_data.charAt(jjj), false) == true) { + // we find the end ... + endPosName = jjj; + } else { + break; + } + tmpPos.check(_data.charAt(jjj)); + } + String tmpname = _data.substring(iii + white + 1, endPosName + 1); + if (parsingProperty.getCaseSensitive() == true) { + tmpname = tmpname.toLowerCase(); + } + Log.debug("find node named : '" + tmpname + "'"); + // find text: + final Object element = this.builder.newElement(parent, tmpname); + _pos.value = endPosName + 1; + _filePos.add(tmpPos); + Log.verbose("start parse : 'element' named='" + tmpname + "'"); + if (iParseElement(element, tmpname, _data, _pos, _filePos, parsingProperty) == false) { + return false; + } + iii = _pos.value; + continue; + } + _filePos.add(tmpPos); + // here we have an error : + parsingProperty.createError(_data, _pos.value, _filePos, "Find an ununderstanding element : '" + _data.charAt(iii + white + 1) + "'"); + return false; + } else { + if (_data.charAt(iii) == '>') { + parsingProperty.createError(_data, _pos.value, _filePos, "Find elemement '>' == > no reason to be here ..."); + return false; + } + // might to be data text ... + if (_data.charAt(iii) == '\n' || _data.charAt(iii) == ' ' || _data.charAt(iii) == '\t' || _data.charAt(iii) == '\r') { + // empty spaces == > nothing to do .... + } else { + // find data == > parse it... + _pos.value = iii; + _filePos.add(tmpPos); + if (iParseText(parent, _data, _pos, _filePos, parsingProperty) == false) { + return false; + } + iii = _pos.value; + } + } + } + if (nameElement == null) { + return true; + } + parsingProperty.createError(_data, _pos.value, _filePos, "Did not find end of the exml::internal::Element : '" + nameElement + "'"); + return false; + } +} diff --git a/src/org/atriasoft/exml/parser/ParsingProperty.java b/src/org/atriasoft/exml/parser/ParsingProperty.java new file mode 100644 index 0000000..33b4578 --- /dev/null +++ b/src/org/atriasoft/exml/parser/ParsingProperty.java @@ -0,0 +1,87 @@ +package org.atriasoft.exml.parser; + +import org.atriasoft.exml.internal.FilePos; +import org.atriasoft.exml.internal.Log; +import org.atriasoft.exml.internal.Tools; + +public class ParsingProperty { + private boolean caseSensitive; //!< check the case sensitive of the nodes and attribute + private boolean writeErrorWhenDetexted; //!< Request print error in parsing just when detected + + private String comment; //!< Comment on the error; + private String Line; //!< Parse line error (copy); + private FilePos filePos; //!< position of the error + + /** + * Constructor + */ + public ParsingProperty() { + this.caseSensitive = false; + this.writeErrorWhenDetexted = true; + this.comment = ""; + this.Line = ""; + this.filePos = new FilePos(0, 0); + } + + /** + * Create an error in the parsing (call by the syetm for error management) + * @param[in] _data string of chat is wrong + * @param[in] _pos Position in the file + * @param[in] _filePos human position of the error + * @param[in] _comment Error string to display + */ + public void createError(final String _data, final int _pos, final FilePos _filePos, final String _comment) { + this.comment = _comment; + this.Line = Tools.extract_line(_data, _pos); + this.filePos = _filePos; + if (this.writeErrorWhenDetexted == true) { + displayError(); + } + } + + /** + * Request display in log of the error + */ + public void displayError() { + if (this.comment.length() == 0) { + Log.error("No error detected ???"); + return; + } + Log.error(this.filePos + " " + this.comment + "\n" + this.Line + "\n" + Tools.createPosPointer(this.Line, this.filePos.getCol())); + //Log.critical("detect error"); + } + + /** + * get the status of case sensitive mode. + * @return true if case sensitive is active + */ + public boolean getCaseSensitive() { + return this.caseSensitive; + } + + /** + * Get the display of the error status. + * @return true Display error + * @return false Does not display error (get it at end) + */ + public boolean getDisplayError() { + return this.writeErrorWhenDetexted; + } + + /** + * Enable or diasable the case sensitive (must be done before the call of parsing) + * @param[in] _val true if enable; false else. + */ + // TODO: Naming error, it is insensitive ... + public void setCaseSensitive(final boolean _val) { + this.caseSensitive = _val; + } + + /** + * Set the display of the error when detected. + * @param[in] _value true: display error, false not display error (get it at end) + */ + public void setDisplayError(final boolean _value) { + this.writeErrorWhenDetexted = _value; + } +} diff --git a/test/src/test/atriasoft/exml/ExmlLocal.java b/test/src/test/atriasoft/exml/ExmlLocal.java index 41d0e51..08b539d 100644 --- a/test/src/test/atriasoft/exml/ExmlLocal.java +++ b/test/src/test/atriasoft/exml/ExmlLocal.java @@ -6,6 +6,7 @@ package test.atriasoft.exml; import org.atriasoft.exml.Document; +import org.atriasoft.exml.Exml; import org.junit.jupiter.api.Assertions; class ExmlLocal { @@ -18,7 +19,7 @@ class ExmlLocal { final Document doc = new Document(); //doc.setCaseSensitive(!_caseInSensitive); Log.verbose("parse : \n" + _input); - final boolean retParse = doc.parse(_input); + final boolean retParse = Exml.parse(_input); if (_errorPos == 1) { Assertions.assertEquals(retParse, false); return; diff --git a/test/src/test/atriasoft/exml/ExmlTestAttribute.java b/test/src/test/atriasoft/exml/ExmlTestAttribute.java index 996564d..ee5316f 100644 --- a/test/src/test/atriasoft/exml/ExmlTestAttribute.java +++ b/test/src/test/atriasoft/exml/ExmlTestAttribute.java @@ -5,9 +5,9 @@ */ package test.atriasoft.exml; -import org.atriasoft.exml.Attribute; +import org.atriasoft.exml.XmlAttribute; import org.atriasoft.exml.Document; -import org.atriasoft.exml.Element; +import org.atriasoft.exml.XmlElement; import org.atriasoft.exml.exception.ExmlAttributeDoesNotExist; import org.atriasoft.exml.exception.ExmlNodeDoesNotExist; import org.junit.jupiter.api.Assertions; @@ -22,29 +22,29 @@ public class ExmlTestAttribute { @Test public void AttributeElementNotExist() { - final Element myElement = new Element("NodeName"); + final XmlElement myElement = new XmlElement("NodeName"); Assertions.assertThrows(ExmlAttributeDoesNotExist.class, () -> myElement.getAttr(65465465)); } @Test public void clear() { - final Attribute myAttribute = new Attribute("nameAttribute", "valueAttribute"); + final XmlAttribute myAttribute = new XmlAttribute("nameAttribute", "valueAttribute"); myAttribute.clear(); } @Test public void createAssignement() { - final Attribute myAttribute = new Attribute("nameAttribute", "valueAttribute"); - final Attribute myOtherAttribute = myAttribute.clone(); + final XmlAttribute myAttribute = new XmlAttribute("nameAttribute", "valueAttribute"); + final XmlAttribute myOtherAttribute = myAttribute.clone(); Assertions.assertEquals(myAttribute.getValue(), myOtherAttribute.getValue()); Assertions.assertEquals(myAttribute.getName(), myOtherAttribute.getName()); } @Test public void createCopy() { - final Attribute myAttribute = new Attribute("nameAttribute", "valueAttribute"); - final Attribute myOtherAttribute = new Attribute(myAttribute); + final XmlAttribute myAttribute = new XmlAttribute("nameAttribute", "valueAttribute"); + final XmlAttribute myOtherAttribute = new XmlAttribute(myAttribute); Assertions.assertEquals(myAttribute.getValue(), myOtherAttribute.getValue()); Assertions.assertEquals(myAttribute.getName(), myOtherAttribute.getName()); } @@ -52,7 +52,7 @@ public class ExmlTestAttribute { @Test public void exist() { - final Element elem = new Element("elem"); + final XmlElement elem = new XmlElement("elem"); elem.setAttribute("valA", "plop"); Assertions.assertEquals(elem.existAttribute("valA"), true); Assertions.assertEquals(elem.existAttribute("qsdfsdf"), false); @@ -61,7 +61,7 @@ public class ExmlTestAttribute { @Test public void get() { - final Element elem = new Element("elem"); + final XmlElement elem = new XmlElement("elem"); elem.setAttribute("valA", "plop"); try { Assertions.assertEquals(elem.getAttribute("valA"), "plop"); @@ -74,7 +74,7 @@ public class ExmlTestAttribute { @Test public void getpair() { - final Element elem = new Element("elem"); + final XmlElement elem = new XmlElement("elem"); elem.setAttribute("valA", "coucou"); try { Assertions.assertEquals(elem.getAttrPair(0).first, "valA"); @@ -90,14 +90,14 @@ public class ExmlTestAttribute { public void moveInAllElement() { final Document doc = new Document(); doc.parse(""); - Element elem; + XmlElement elem; try { - elem = (Element) doc.getNode("elem"); + elem = (XmlElement) doc.getNode("elem"); } catch (final ExmlNodeDoesNotExist e) { Assertions.fail("Should Not have thrown an exception"); return; } - for (final Attribute it : elem.getAttributes()) { + for (final XmlAttribute it : elem.getAttributes()) { Assertions.assertEquals(it.getName(), "valA"); Assertions.assertEquals(it.getValue(), "plop"); } @@ -105,7 +105,7 @@ public class ExmlTestAttribute { @Test public void remove() { - final Element elem = new Element("elem"); + final XmlElement elem = new XmlElement("elem"); elem.setAttribute("valA", "plop"); Assertions.assertEquals(elem.existAttribute("valA"), true); elem.removeAttribute("valA"); @@ -115,7 +115,7 @@ public class ExmlTestAttribute { @Test public void setGetName() { - final Attribute myAttribute = new Attribute("nameAttribute", "valueAttribute"); + final XmlAttribute myAttribute = new XmlAttribute("nameAttribute", "valueAttribute"); Assertions.assertEquals(myAttribute.getName(), "nameAttribute"); myAttribute.setName("newName"); Assertions.assertEquals(myAttribute.getName(), "newName"); @@ -124,7 +124,7 @@ public class ExmlTestAttribute { @Test public void setGetValue() { - final Attribute myAttribute = new Attribute("nameAttribute", "valueAttribute"); + final XmlAttribute myAttribute = new XmlAttribute("nameAttribute", "valueAttribute"); Assertions.assertEquals(myAttribute.getValue(), "valueAttribute"); myAttribute.setValue("new value"); Assertions.assertEquals(myAttribute.getValue(), "new value"); @@ -133,7 +133,7 @@ public class ExmlTestAttribute { @Test public void setterNew() { - final Element elem = new Element("elem"); + final XmlElement elem = new XmlElement("elem"); elem.setAttribute("valA", "coucou"); try { Assertions.assertEquals(elem.getAttribute("valA"), "coucou"); @@ -145,7 +145,7 @@ public class ExmlTestAttribute { @Test public void setterRewrite() { - final Element elem = new Element("elem"); + final XmlElement elem = new XmlElement("elem"); elem.setAttribute("valA", "coucou"); try { Assertions.assertEquals(elem.getAttribute("valA"), "coucou"); diff --git a/test/src/test/atriasoft/exml/ExmlTestCData.java b/test/src/test/atriasoft/exml/ExmlTestCData.java index 98c2d05..370085c 100644 --- a/test/src/test/atriasoft/exml/ExmlTestCData.java +++ b/test/src/test/atriasoft/exml/ExmlTestCData.java @@ -6,7 +6,7 @@ package test.atriasoft.exml; import org.atriasoft.exml.Document; -import org.atriasoft.exml.Element; +import org.atriasoft.exml.XmlElement; import org.atriasoft.exml.TextCDATA; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; @@ -32,8 +32,8 @@ public class ExmlTestCData { public void parseCDATA() { final Document doc = new Document(); doc.parse("examp]le]] ...]]>"); - final Element elem = Assertions.assertDoesNotThrow(() -> { - return (Element) doc.getNode("elem"); + final XmlElement elem = Assertions.assertDoesNotThrow(() -> { + return (XmlElement) doc.getNode("elem"); }); Assertions.assertEquals("Text &é<>examp]le]] ...", elem.getText()); } diff --git a/test/src/test/atriasoft/exml/ExmlTestComment.java b/test/src/test/atriasoft/exml/ExmlTestComment.java index 1328c4b..fd652c4 100644 --- a/test/src/test/atriasoft/exml/ExmlTestComment.java +++ b/test/src/test/atriasoft/exml/ExmlTestComment.java @@ -5,9 +5,9 @@ */ package test.atriasoft.exml; -import org.atriasoft.exml.Comment; -import org.atriasoft.exml.Node; -import org.atriasoft.exml.NodeType; +import org.atriasoft.exml.XmlComment; +import org.atriasoft.exml.XmlNode; +import org.atriasoft.exml.XmlNodeType; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -20,28 +20,28 @@ public class ExmlTestComment { @Test public void create() { - final Comment myComment = new Comment("my Comment"); - Assertions.assertEquals(myComment.getType(), NodeType.COMMENT); + final XmlComment myComment = new XmlComment("my Comment"); + Assertions.assertEquals(myComment.getType(), XmlNodeType.COMMENT); } @Test public void createAssignement() { - final Comment myComment = new Comment("my comment"); - final Comment myOtherComment = myComment.clone(); + final XmlComment myComment = new XmlComment("my comment"); + final XmlComment myOtherComment = myComment.clone(); Assertions.assertEquals(myComment.getValue(), myOtherComment.getValue()); } @Test public void createCopy() { - final Comment myComment = new Comment("my Comment"); - final Comment myOtherComment = new Comment(myComment); + final XmlComment myComment = new XmlComment("my Comment"); + final XmlComment myOtherComment = new XmlComment(myComment); Assertions.assertEquals(myComment.getValue(), myOtherComment.getValue()); } @Test public void transform() { - Comment myComment = new Comment("my comment"); - final Node myNode = myComment; + XmlComment myComment = new XmlComment("my comment"); + final XmlNode myNode = myComment; myComment = myNode.toComment(); Assertions.assertEquals(myComment.getValue(), "my comment"); } diff --git a/test/src/test/atriasoft/exml/ExmlTestDeclarationXML.java b/test/src/test/atriasoft/exml/ExmlTestDeclarationXML.java index 99a06ea..9ca027b 100644 --- a/test/src/test/atriasoft/exml/ExmlTestDeclarationXML.java +++ b/test/src/test/atriasoft/exml/ExmlTestDeclarationXML.java @@ -6,7 +6,7 @@ package test.atriasoft.exml; import org.atriasoft.exml.DeclarationXML; -import org.atriasoft.exml.NodeType; +import org.atriasoft.exml.XmlNodeType; import org.atriasoft.exml.exception.ExmlAttributeDoesNotExist; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; @@ -21,7 +21,7 @@ public class ExmlTestDeclarationXML { @Test public void create() { final DeclarationXML myDeclarationXML = new DeclarationXML("1.0", "UTF-8", true); - Assertions.assertEquals(myDeclarationXML.getType(), NodeType.DECLARATION); + Assertions.assertEquals(myDeclarationXML.getType(), XmlNodeType.DECLARATION); try { Assertions.assertEquals(myDeclarationXML.getAttribute("version"), "1.0"); Assertions.assertEquals(myDeclarationXML.getAttribute("encoding"), "UTF-8"); diff --git a/test/src/test/atriasoft/exml/ExmlTestElement.java b/test/src/test/atriasoft/exml/ExmlTestElement.java index 09950d5..dfa9ac6 100644 --- a/test/src/test/atriasoft/exml/ExmlTestElement.java +++ b/test/src/test/atriasoft/exml/ExmlTestElement.java @@ -6,9 +6,9 @@ package test.atriasoft.exml; import org.atriasoft.exml.Document; -import org.atriasoft.exml.Element; -import org.atriasoft.exml.Node; -import org.atriasoft.exml.NodeType; +import org.atriasoft.exml.XmlElement; +import org.atriasoft.exml.XmlNode; +import org.atriasoft.exml.XmlNodeType; import org.atriasoft.exml.exception.ExmlNodeDoesNotExist; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; @@ -22,9 +22,9 @@ public class ExmlTestElement { @Test public void append() { - final Element myElement = new Element("NodeName"); + final XmlElement myElement = new XmlElement("NodeName"); Assertions.assertEquals(myElement.getNodes().size(), 0); - myElement.append(new Element("jkjhkjhkh")); + myElement.append(new XmlElement("jkjhkjhkh")); Assertions.assertEquals(myElement.getNodes().size(), 1); try { myElement.getNode("jkjhkjhkh"); @@ -36,9 +36,9 @@ public class ExmlTestElement { @Test public void clear() { - final Element myElement = new Element("NodeName"); + final XmlElement myElement = new XmlElement("NodeName"); Assertions.assertEquals(myElement.getNodes().size(), 0); - myElement.append(new Element("jkjhkjhkh")); + myElement.append(new XmlElement("jkjhkjhkh")); Assertions.assertEquals(myElement.getNodes().size(), 1); try { myElement.getNode("jkjhkjhkh"); @@ -52,15 +52,15 @@ public class ExmlTestElement { @Test public void create() { - final Element myElement = new Element("NodeName"); - Assertions.assertEquals(myElement.getType(), NodeType.ELEMENT); + final XmlElement myElement = new XmlElement("NodeName"); + Assertions.assertEquals(myElement.getType(), XmlNodeType.ELEMENT); } @Test public void createAssignement() { - final Element myElement = new Element("NodeName"); + final XmlElement myElement = new XmlElement("NodeName"); try { - final Element myOtherElement = myElement.clone(); + final XmlElement myOtherElement = myElement.clone(); Assertions.assertEquals(myElement.getValue(), myOtherElement.getValue()); } catch (final CloneNotSupportedException e) { Assertions.fail("Should Not have thrown an exception"); @@ -69,9 +69,9 @@ public class ExmlTestElement { @Test public void createCopy() { - final Element myElement = new Element("NodeName"); + final XmlElement myElement = new XmlElement("NodeName"); try { - final Element myOtherElement = new Element(myElement); + final XmlElement myOtherElement = new XmlElement(myElement); Assertions.assertEquals(myElement.getValue(), myOtherElement.getValue()); } catch (final CloneNotSupportedException e) { Assertions.fail("Should Not have thrown an exception"); @@ -81,34 +81,34 @@ public class ExmlTestElement { @Test public void getNamed() { - final Element myElement = new Element("NodeName"); + final XmlElement myElement = new XmlElement("NodeName"); Assertions.assertEquals(myElement.existNode("jkjhkjhkh"), false); } @Test public void getNodeId() { - final Element myElement = new Element("NodeName"); + final XmlElement myElement = new XmlElement("NodeName"); Assertions.assertEquals(false, myElement.existNode(465)); } @Test public void getText1() { - final Element myElement = new Element("NodeName"); + final XmlElement myElement = new XmlElement("NodeName"); Assertions.assertEquals("", myElement.getText()); } @Test public void getText2() { - final Element myElement = new Element("NodeName"); - myElement.append(new Element("jkjhkjhkh")); + final XmlElement myElement = new XmlElement("NodeName"); + myElement.append(new XmlElement("jkjhkjhkh")); Assertions.assertEquals("\n", myElement.getText()); } @Test public void getTypeId() { - final Element myElement = new Element("NodeName"); + final XmlElement myElement = new XmlElement("NodeName"); Assertions.assertThrows(ExmlNodeDoesNotExist.class, () -> myElement.getType(1)); } @@ -116,8 +116,8 @@ public class ExmlTestElement { public void moveInAllElement() { final Document doc = new Document(); doc.parse(""); - for (final Node it : doc.getNodes()) { - final Element elem = (Element) it; + for (final XmlNode it : doc.getNodes()) { + final XmlElement elem = (XmlElement) it; Assertions.assertEquals("elem", elem.getValue()); Assertions.assertEquals(2, elem.getNodes().size()); Assertions.assertEquals(2, elem.size());