[DEV] refacto separate serializer, parser and model is OK ==> sax reader is OK not the sax generator (API not open)

This commit is contained in:
Edouard DUPIN 2021-02-26 00:13:25 +01:00
parent d16f4c95e3
commit f610c366ba
29 changed files with 481 additions and 404 deletions

View File

@ -6,6 +6,10 @@
open module org.atriasoft.exml { open module org.atriasoft.exml {
exports org.atriasoft.exml; exports org.atriasoft.exml;
exports org.atriasoft.exml.model;
exports org.atriasoft.exml.exception;
exports org.atriasoft.exml.builder;
exports org.atriasoft.exml.parser;
requires transitive org.atriasoft.etk; requires transitive org.atriasoft.etk;
requires transitive io.scenarium.logger; requires transitive io.scenarium.logger;

View File

@ -9,8 +9,13 @@ package org.atriasoft.exml;
import org.atriasoft.exml.builder.Builder; import org.atriasoft.exml.builder.Builder;
import org.atriasoft.exml.builder.BuilderGeneric; import org.atriasoft.exml.builder.BuilderGeneric;
import org.atriasoft.exml.exception.ExmlBuilderException; import org.atriasoft.exml.exception.ExmlBuilderException;
import org.atriasoft.exml.exception.ExmlParserErrorMulti;
import org.atriasoft.exml.internal.Log; import org.atriasoft.exml.internal.Log;
import org.atriasoft.exml.model.XmlElement;
import org.atriasoft.exml.model.XmlNode;
import org.atriasoft.exml.parser.ParseXml; import org.atriasoft.exml.parser.ParseXml;
import org.atriasoft.exml.parser.ParsingProperty;
import org.atriasoft.exml.serializer.SerializerXml;
public class Exml { public class Exml {
/** /**
@ -28,35 +33,21 @@ public class Exml {
* @return false : An error occured * @return false : An error occured
* @return true : Parsing is OK * @return true : Parsing is OK
*/ */
public static boolean generate(final StringBuilder _data) { public static void generate(final XmlNode root, final StringBuilder _data) {
if (root.isElement() == false || ((XmlElement) root).getValue().isEmpty() == false) {
SerializerXml.serialize(root, _data, 0);
} else {
SerializerXml.serializeRoot((XmlElement) root, _data);
}
//return iGenerate(_data, 0); //return iGenerate(_data, 0);
return false;
} }
public static boolean iGenerate(final StringBuilder _data, final int _indent) { public static XmlElement parse(final String data) throws ExmlBuilderException, ExmlParserErrorMulti {
/*
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 Builder builder = new BuilderGeneric();
final ParseXml parser = new ParseXml(builder); final ParseXml parser = new ParseXml(builder);
final ParsingProperty property = new ParsingProperty();
Object out = null; property.setDisplayError(true);
try { return (XmlElement) parser.parse(data, property);
out = parser.parse(data);
} catch (final ExmlBuilderException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return (XmlNode) out;
} }
/** /**

View File

@ -1,25 +0,0 @@
/** @file
* @author Edouard DUPIN
* @copyright 2021, Edouard DUPIN, all right reserved
* @license MPL v2.0 (see license file)
*/
package org.atriasoft.exml;
public class TextCDATA extends XmlText {
public TextCDATA() {
super();
}
public TextCDATA(final String data) {
super(data);
}
@Override
protected boolean iGenerate(final StringBuilder _data, final int _indent) {
_data.append("<![CDATA[");
_data.append(this.value);
_data.append("]]>");
return true;
}
}

View File

@ -1,15 +1,12 @@
/** @file
* @author Edouard DUPIN
* @copyright 2021, Edouard DUPIN, all right reserved
* @license MPL v2.0 (see license file)
*/
package org.atriasoft.exml.builder; package org.atriasoft.exml.builder;
import org.atriasoft.exml.exception.ExmlBuilderException; import org.atriasoft.exml.exception.ExmlBuilderException;
/**
* @author heero
*
*/
/**
* @author heero
*
*/
public interface Builder { public interface Builder {
/** /**

View File

@ -1,12 +1,17 @@
/** @file
* @author Edouard DUPIN
* @copyright 2021, Edouard DUPIN, all right reserved
* @license MPL v2.0 (see license file)
*/
package org.atriasoft.exml.builder; 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; import org.atriasoft.exml.exception.ExmlBuilderException;
import org.atriasoft.exml.model.XmlAttribute;
import org.atriasoft.exml.model.XmlAttributeList;
import org.atriasoft.exml.model.XmlComment;
import org.atriasoft.exml.model.XmlDeclaration;
import org.atriasoft.exml.model.XmlElement;
import org.atriasoft.exml.model.XmlText;
public class BuilderGeneric implements Builder { public class BuilderGeneric implements Builder {
@ -15,6 +20,7 @@ public class BuilderGeneric implements Builder {
if (element instanceof XmlElement) { if (element instanceof XmlElement) {
final XmlElement elem = (XmlElement) element; final XmlElement elem = (XmlElement) element;
elem.append(new XmlComment(comment)); elem.append(new XmlComment(comment));
return;
} }
throw new ExmlBuilderException("can not add Comment on something else than Element"); throw new ExmlBuilderException("can not add Comment on something else than Element");
} }

View File

@ -0,0 +1,25 @@
package org.atriasoft.exml.exception;
import org.atriasoft.exml.parser.FilePos;
public class ExmlParserError extends ExmlBuilderException {
private static final long serialVersionUID = 1L;
private final String dataLine; //!< Parse line error (copy);
private final FilePos filePos; //!< position of the error
public ExmlParserError(final String dataLine, final FilePos filePos, final String comment) {
super(comment);
this.dataLine = dataLine;
this.filePos = filePos.clone();
}
public String getDataLine() {
return this.dataLine;
}
public FilePos getFilePos() {
return this.filePos;
}
}

View File

@ -0,0 +1,18 @@
package org.atriasoft.exml.exception;
import java.util.List;
public class ExmlParserErrorMulti extends ExmlBuilderException {
private static final long serialVersionUID = 1L;
private final List<ExmlParserError> errors; // list of errors
public ExmlParserErrorMulti(final String message, final List<ExmlParserError> errors) {
super(message);
this.errors = errors;
}
public List<ExmlParserError> getErrors() {
return this.errors;
}
}

View File

@ -3,9 +3,9 @@
* @copyright 2021, Edouard DUPIN, all right reserved * @copyright 2021, Edouard DUPIN, all right reserved
* @license MPL v2.0 (see license file) * @license MPL v2.0 (see license file)
*/ */
package org.atriasoft.exml; package org.atriasoft.exml.model;
import org.atriasoft.exml.internal.FilePos; import org.atriasoft.exml.parser.FilePos;
/** /**
* Single attribute element * Single attribute element
@ -68,15 +68,6 @@ public class XmlAttribute {
return this.value; 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 * set the name of the attribute
* @param[in] _name New name of the attribute * @param[in] _name New name of the attribute

View File

@ -3,9 +3,10 @@
* @copyright 2021, Edouard DUPIN, all right reserved * @copyright 2021, Edouard DUPIN, all right reserved
* @license MPL v2.0 (see license file) * @license MPL v2.0 (see license file)
*/ */
package org.atriasoft.exml; package org.atriasoft.exml.model;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.ListIterator; import java.util.ListIterator;
@ -108,7 +109,7 @@ public abstract class XmlAttributeList extends XmlNode {
} }
public List<XmlAttribute> getAttributes() { public List<XmlAttribute> getAttributes() {
return this.listAttribute; return Collections.unmodifiableList(this.listAttribute);
} }
/** /**
@ -122,16 +123,6 @@ public abstract class XmlAttributeList extends XmlNode {
return new Pair<String, String>(att.getName(), att.getValue()); return new Pair<String, String>(att.getName(), att.getValue());
} }
@Override
protected boolean iGenerate(final StringBuilder _data, final int _indent) {
for (int iii = 0; iii < this.listAttribute.size(); iii++) {
if (this.listAttribute.get(iii) != null) {
this.listAttribute.get(iii).iGenerate(_data, _indent);
}
}
return true;
}
/** /**
* Remove an attribute form the list * Remove an attribute form the list
* @param[in] _name Name of the attribute * @param[in] _name Name of the attribute

View File

@ -3,9 +3,7 @@
* @copyright 2021, Edouard DUPIN, all right reserved * @copyright 2021, Edouard DUPIN, all right reserved
* @license MPL v2.0 (see license file) * @license MPL v2.0 (see license file)
*/ */
package org.atriasoft.exml; package org.atriasoft.exml.model;
import org.atriasoft.exml.internal.Tools;
/** /**
* Comment node: lt;!-- ... --gt; * Comment node: lt;!-- ... --gt;
@ -38,13 +36,4 @@ public class XmlComment extends XmlNode {
return XmlNodeType.COMMENT; return XmlNodeType.COMMENT;
} }
@Override
public boolean iGenerate(final StringBuilder _data, final int _indent) {
Tools.addIndent(_data, _indent);
_data.append("<!--");
_data.append(this.value);
_data.append("-->\n");
return true;
}
}; };

View File

@ -3,10 +3,9 @@
* @copyright 2021, Edouard DUPIN, all right reserved * @copyright 2021, Edouard DUPIN, all right reserved
* @license MPL v2.0 (see license file) * @license MPL v2.0 (see license file)
*/ */
package org.atriasoft.exml; package org.atriasoft.exml.model;
import org.atriasoft.exml.internal.Log; import org.atriasoft.exml.internal.Log;
import org.atriasoft.exml.internal.Tools;
/** /**
* Declaration node: lt;?XXXXXX ... gt; * Declaration node: lt;?XXXXXX ... gt;
@ -57,16 +56,5 @@ public class XmlDeclaration extends XmlAttributeList {
@Override @Override
public XmlNodeType getType() { public XmlNodeType getType() {
return XmlNodeType.DECLARATION; return XmlNodeType.DECLARATION;
};
@Override
protected boolean iGenerate(final StringBuilder _data, final int _indent) {
Tools.addIndent(_data, _indent);
_data.append("<?");
_data.append(this.value);
super.iGenerate(_data, _indent);
_data.append("?>\n");
return true;
} }
} }

View File

@ -3,15 +3,16 @@
* @copyright 2021, Edouard DUPIN, all right reserved * @copyright 2021, Edouard DUPIN, all right reserved
* @license MPL v2.0 (see license file) * @license MPL v2.0 (see license file)
*/ */
package org.atriasoft.exml; package org.atriasoft.exml.model;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.ListIterator; import java.util.ListIterator;
import org.atriasoft.exml.exception.ExmlNodeDoesNotExist; import org.atriasoft.exml.exception.ExmlNodeDoesNotExist;
import org.atriasoft.exml.internal.Log; import org.atriasoft.exml.internal.Log;
import org.atriasoft.exml.internal.Tools; import org.atriasoft.exml.serializer.SerializerXml;
/** @file /** @file
* @author Edouard DUPIN * @author Edouard DUPIN
@ -147,12 +148,12 @@ public class XmlElement extends XmlAttributeList {
* @return List of current nodes. * @return List of current nodes.
*/ */
public List<XmlNode> getNodes() { public List<XmlNode> getNodes() {
return this.listSub; return Collections.unmodifiableList(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 <![CDATA[...]]> * 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 <![CDATA[...]]>
* @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... * @return the current data string. if Only one text node, then we get the parsed data (no amp; ...) if more than one node, then we transform ,",',<,> in xml normal text...
*/ */
public String getText() { public String getText() {
final StringBuilder res = new StringBuilder(); final StringBuilder res = new StringBuilder();
@ -160,12 +161,12 @@ public class XmlElement extends XmlAttributeList {
if (this.listSub.get(0).getType() == XmlNodeType.TEXT) { if (this.listSub.get(0).getType() == XmlNodeType.TEXT) {
res.append(this.listSub.get(0).getValue()); res.append(this.listSub.get(0).getValue());
} else { } else {
this.listSub.get(0).iGenerate(res, 0); SerializerXml.serialize(this.listSub.get(0), res, 0);
} }
} else { } else {
for (int iii = 0; iii < this.listSub.size(); iii++) { for (int iii = 0; iii < this.listSub.size(); iii++) {
if (this.listSub.get(iii) != null) { if (this.listSub.get(iii) != null) {
this.listSub.get(iii).iGenerate(res, 0); SerializerXml.serialize(this.listSub.get(iii), res, 0);
} }
} }
} }
@ -190,36 +191,6 @@ public class XmlElement extends XmlAttributeList {
return this.listSub.get(_id).getType(); 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("</");
_data.append(this.value);
_data.append(">\n");
} else {
_data.append("/>\n");
}
return true;
}
/** /**
* Remove all element with this name * Remove all element with this name
* @param[in] _nodeName Name of nodes to remove. * @param[in] _nodeName Name of nodes to remove.

View File

@ -3,63 +3,48 @@
* @copyright 2021, Edouard DUPIN, all right reserved * @copyright 2021, Edouard DUPIN, all right reserved
* @license MPL v2.0 (see license file) * @license MPL v2.0 (see license file)
*/ */
package org.atriasoft.exml; package org.atriasoft.exml.model;
import org.atriasoft.exml.internal.FilePos;
/** /**
* Basic main object of all xml elements. * Basic main object of all xml elements.
*/ */
public abstract class XmlNode { public abstract class XmlNode {
/// Value of the node (for element this is the name, for text it is the inside text ...);
protected FilePos pos; //!< position in the read file (null if the file is not parsed) protected String value = "";
protected String value; //!< value of the node (for element this is the name, for text it is the inside text ...);
/** /**
* basic element of a xml structure * basic element of a xml structure
*/ */
public XmlNode() { public XmlNode() {}
this.pos = null;
}
/** /**
* basic element of a xml structure * Basic element of a xml structure
* @param[in] _value value of the node * @param[in] _value value of the node
*/ */
public XmlNode(final String _value) { public XmlNode(final String _value) {
this.pos = null;
this.value = _value; this.value = _value;
} }
/** /**
* clear the Node * Clear the Node
*/ */
public void clear() { public void clear() {
this.value = ""; this.value = "";
this.pos = null;
} }
@Override @Override
protected XmlNode clone() throws CloneNotSupportedException { protected XmlNode clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException("Can not clone an abstract class ..."); throw new CloneNotSupportedException("Can not clone an abs ctract class ...");
} }
/** /**
* get the current position where the element is in the file * Get the node type.
* @return The file position reference
*/
public FilePos getPos() {
return this.pos;
}
/**
* get the node type.
* @return the type of the Node. * @return the type of the Node.
*/ */
public abstract XmlNodeType getType(); public abstract XmlNodeType getType();
/** /**
* get the current element Value. * Get the current element Value.
* @return the reference of the string value. * @return the reference of the string value.
*/ */
public String getValue() { public String getValue() {
@ -67,26 +52,7 @@ public abstract class XmlNode {
} }
/** /**
* generate a string with the tree of the xml * Check if the node is a Comment
* @param[in,out] _data string where to add the elements
* @param[in] _indent current indentation of the file
* @return false if an error occured.
*/
protected abstract boolean iGenerate(final StringBuilder _data, final int _indent);
/**
* parse the Current node [pure VIRUAL]
* @param[in] _data data string to parse.
* @param[in,out] _pos position in the string to start parse, return the position end of parsing.
* @param[in] _caseSensitive Request a parsion of element that is not case sensitive (all element is in low case)
* @param[in,out] _filePos file parsing position (line x col x)
* @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);
/**
* check if the node is a Comment
* @return true if the node is a Comment * @return true if the node is a Comment
*/ */
public final boolean isComment() { public final boolean isComment() {
@ -101,14 +67,6 @@ public abstract class XmlNode {
return this instanceof XmlDeclaration; return this instanceof XmlDeclaration;
} }
/**
* check if the node is a Document
* @return true if the node is a Document
*/
public final boolean isDocument() {
return this instanceof Document;
}
/** /**
* check if the node is a Element * check if the node is a Element
* @return true if the node is a Element * @return true if the node is a Element
@ -129,7 +87,6 @@ public abstract class XmlNode {
* set the value of the node. * set the value of the node.
* @param[in] _value New value of the node. * @param[in] _value New value of the node.
*/ */
public final void setValue(final String _value) { public final void setValue(final String _value) {
this.value = _value; this.value = _value;
} }
@ -150,14 +107,6 @@ public abstract class XmlNode {
return (XmlDeclaration) this; return (XmlDeclaration) this;
} }
/**
* Cast the element in a Document if it is possible.
* @return pointer on the class or null.
*/
public final Document toDocument() {
return (Document) this;
}
/** /**
* Cast the element in a Element if it is possible. * Cast the element in a Element if it is possible.
* @return pointer on the class or null. * @return pointer on the class or null.

View File

@ -3,13 +3,12 @@
* @copyright 2021, Edouard DUPIN, all right reserved * @copyright 2021, Edouard DUPIN, all right reserved
* @license MPL v2.0 (see license file) * @license MPL v2.0 (see license file)
*/ */
package org.atriasoft.exml; package org.atriasoft.exml.model;
/** /**
* Type of the XML elements. * Type of the XML elements.
*/ */
public enum XmlNodeType { public enum XmlNodeType {
DOCUMENT, //!< all the file main access
DECLARATION, //!< &lt;?xml ... ?&gt; DECLARATION, //!< &lt;?xml ... ?&gt;
ELEMENT, //!< the &lt;XXX&gt; ... &lt;/XXX&gt; ELEMENT, //!< the &lt;XXX&gt; ... &lt;/XXX&gt;
COMMENT, //!< comment node : &lt;!-- --&gt; COMMENT, //!< comment node : &lt;!-- --&gt;

View File

@ -3,7 +3,7 @@
* @copyright 2021, Edouard DUPIN, all right reserved * @copyright 2021, Edouard DUPIN, all right reserved
* @license MPL v2.0 (see license file) * @license MPL v2.0 (see license file)
*/ */
package org.atriasoft.exml; package org.atriasoft.exml.model;
/** /**
* Text node interface (internal data between two Marker: &lt;XXX&gt; ALL here &lt;/XXX&gt; * Text node interface (internal data between two Marker: &lt;XXX&gt; ALL here &lt;/XXX&gt;
@ -27,7 +27,7 @@ public class XmlText extends XmlNode {
* count the number of line in the current text * count the number of line in the current text
* @return The number of lines * @return The number of lines
*/ */
protected int countLines() { public int countLines() {
int count = 1; int count = 1;
for (int iii = 0; iii < this.value.length(); iii++) { for (int iii = 0; iii < this.value.length(); iii++) {
if (this.value.charAt(iii) == '\n') { if (this.value.charAt(iii) == '\n') {
@ -40,12 +40,6 @@ public class XmlText extends XmlNode {
@Override @Override
public XmlNodeType getType() { public XmlNodeType getType() {
return XmlNodeType.TEXT; return XmlNodeType.TEXT;
};
@Override
protected boolean iGenerate(final StringBuilder _data, final int _indent) {
_data.append(replaceSpecialCharOut(this.value));
return true;
} }
}; };

View File

@ -3,7 +3,7 @@
* @copyright 2021, Edouard DUPIN, all right reserved * @copyright 2021, Edouard DUPIN, all right reserved
* @license MPL v2.0 (see license file) * @license MPL v2.0 (see license file)
*/ */
package org.atriasoft.exml.internal; package org.atriasoft.exml.parser;
/** @file /** @file
* @author Edouard DUPIN * @author Edouard DUPIN

View File

@ -2,10 +2,9 @@ package org.atriasoft.exml.parser;
import org.atriasoft.exml.builder.Builder; import org.atriasoft.exml.builder.Builder;
import org.atriasoft.exml.exception.ExmlBuilderException; import org.atriasoft.exml.exception.ExmlBuilderException;
import org.atriasoft.exml.internal.FilePos; import org.atriasoft.exml.exception.ExmlParserError;
import org.atriasoft.exml.exception.ExmlParserErrorMulti;
import org.atriasoft.exml.internal.Log; import org.atriasoft.exml.internal.Log;
import org.atriasoft.exml.internal.PositionParsing;
import org.atriasoft.exml.internal.Tools;
public class ParseXml { public class ParseXml {
// global builder that is generate the final Tree // global builder that is generate the final Tree
@ -30,27 +29,32 @@ public class ParseXml {
} }
} }
String name = _data.substring(_pos.value, lastElementName + 1); String name = _data.substring(_pos.value, lastElementName + 1);
String value = ""; if (parsingProperty.getStoreMode() == StoreMode.LOWERCASE) {
if (parsingProperty.getCaseSensitive() == true) {
name = name.toLowerCase(); name = name.toLowerCase();
} else if (parsingProperty.getStoreMode() == StoreMode.UPPERCASE) {
name = name.toUpperCase();
} }
// TODO check if in the current list of attribute one is not currently set with this value...
String value = "";
// count white space : // count white space :
final FilePos tmpPos = new FilePos(); final FilePos tmpPos = new FilePos();
int white = Tools.countWhiteChar(_data, lastElementName + 1, tmpPos); int white = Tools.countWhiteChar(_data, lastElementName + 1, tmpPos);
_filePos.add(tmpPos); _filePos.add(tmpPos);
if (lastElementName + white + 1 >= _data.length()) { if (lastElementName + white + 1 >= _data.length()) {
parsingProperty.createError(_data, lastElementName + white + 1, _filePos, " parse an xml end with an attribute parsing..."); parsingProperty.createError(new ExmlParserError(Tools.extractLine(_data, lastElementName + white + 1), _filePos, " parse an xml end with an attribute parsing..."));
return false; return false;
} }
if (_data.charAt(lastElementName + white + 1) != '=') { if (_data.charAt(lastElementName + white + 1) != '=') {
parsingProperty.createError(_data, lastElementName + white + 1, _filePos, " error attribute parsing == > missing '=' ..."); parsingProperty.createError(new ExmlParserError(Tools.extractLine(_data, lastElementName + white + 1), _filePos, " error attribute parsing == > missing '=' ..."));
return false; return false;
} }
white += Tools.countWhiteChar(_data, lastElementName + white + 2, tmpPos); white += Tools.countWhiteChar(_data, lastElementName + white + 2, tmpPos);
_filePos.add(tmpPos); _filePos.add(tmpPos);
if (lastElementName + white + 2 >= _data.length()) { if (lastElementName + white + 2 >= _data.length()) {
parsingProperty.createError(_data, lastElementName + white + 2, _filePos, " parse an xml end with an attribute parsing..."); parsingProperty.createError(new ExmlParserError(Tools.extractLine(_data, lastElementName + white + 2), _filePos, " parse an xml end with an attribute parsing..."));
return false; return false;
} }
boolean simpleQuoteCase = false; boolean simpleQuoteCase = false;
@ -64,7 +68,7 @@ public class ParseXml {
for (int iii = lastElementName + white + 2; iii < _data.length(); iii++) { for (int iii = lastElementName + white + 2; iii < _data.length(); iii++) {
Tools.drawElementParsed(_data.charAt(iii), _filePos); Tools.drawElementParsed(_data.charAt(iii), _filePos);
if (_filePos.check(_data.charAt(iii)) == true) { if (_filePos.check(_data.charAt(iii)) == true) {
parsingProperty.createError(_data, iii, _filePos, "unexpected '\\n' in an attribute parsing"); parsingProperty.createError(new ExmlParserError(Tools.extractLine(_data, iii), _filePos, "unexpected '\\n' in an attribute parsing"));
return false; return false;
} }
if (_data.charAt(iii) != ' ' && _data.charAt(iii) != '/' && _data.charAt(iii) != '?' && _data.charAt(iii) != '>') { if (_data.charAt(iii) != ' ' && _data.charAt(iii) != '/' && _data.charAt(iii) != '?' && _data.charAt(iii) != '>') {
@ -119,7 +123,7 @@ public class ParseXml {
return true; return true;
} }
} }
parsingProperty.createError(_data, _pos.value, _filePos, "text CDATA got end of file without finding end node"); parsingProperty.createError(new ExmlParserError(Tools.extractLine(_data, _pos.value), _filePos, "text CDATA got end of file without finding end node"));
_pos.value = _data.length(); _pos.value = _data.length();
return false; return false;
} }
@ -156,7 +160,7 @@ public class ParseXml {
} }
} }
_pos.value = _data.length(); _pos.value = _data.length();
parsingProperty.createError(_data, _pos.value, _filePos, "comment got end of file without finding end node"); parsingProperty.createError(new ExmlParserError(Tools.extractLine(_data, _pos.value), _filePos, "comment got end of file without finding end node"));
return false; return false;
} }
@ -171,7 +175,7 @@ public class ParseXml {
} }
if (_data.charAt(iii) == '>' || _data.charAt(iii) == '<') { if (_data.charAt(iii) == '>' || _data.charAt(iii) == '<') {
// an error occured : // an error occured :
parsingProperty.createError(_data, _pos.value, _filePos, " find '>' or '<' instead of '?>'"); parsingProperty.createError(new ExmlParserError(Tools.extractLine(_data, _pos.value), _filePos, " find '>' or '<' instead of '?>'"));
return false; return false;
} }
if (_data.charAt(iii) == '?' && _data.charAt(iii + 1) == '>') { if (_data.charAt(iii) == '?' && _data.charAt(iii + 1) == '>') {
@ -190,7 +194,7 @@ public class ParseXml {
continue; continue;
} }
} }
parsingProperty.createError(_data, _pos.value, _filePos, "Text got end of file without finding end node"); parsingProperty.createError(new ExmlParserError(Tools.extractLine(_data, _pos.value), _filePos, "Text got end of file without finding end node"));
_pos.value = _data.length(); _pos.value = _data.length();
return false; return false;
} }
@ -211,7 +215,7 @@ public class ParseXml {
if (_data.charAt(iii) == '/') { if (_data.charAt(iii) == '/') {
// standalone node or error... // standalone node or error...
if (iii + 1 >= _data.length()) { if (iii + 1 >= _data.length()) {
parsingProperty.createError(_data, _pos.value, _filePos, "Find end of files ... == > bad case"); parsingProperty.createError(new ExmlParserError(Tools.extractLine(_data, _pos.value), _filePos, "Find end of files ... == > bad case"));
return false; return false;
} }
// TODO : Can have white spaces .... // TODO : Can have white spaces ....
@ -220,7 +224,7 @@ public class ParseXml {
return true; return true;
} }
// error // error
parsingProperty.createError(_data, _pos.value, _filePos, "Find / without > char ..."); parsingProperty.createError(new ExmlParserError(Tools.extractLine(_data, _pos.value), _filePos, "Find / without > char ..."));
return false; return false;
} }
if (Tools.checkAvaillable(_data.charAt(iii), true) == true) { if (Tools.checkAvaillable(_data.charAt(iii), true) == true) {
@ -233,11 +237,12 @@ public class ParseXml {
continue; continue;
} }
if (Tools.isWhiteChar(_data.charAt(iii)) == false) { if (Tools.isWhiteChar(_data.charAt(iii)) == false) {
parsingProperty.createError(_data, iii, _filePos, "Find an unknow element : '" + _data.charAt(iii) + "'"); parsingProperty.createError(new ExmlParserError(Tools.extractLine(_data, iii), _filePos, "Find an unknow element : '" + _data.charAt(iii) + "'"));
return false; 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"); parsingProperty.createError(new ExmlParserError(Tools.extractLine(_data, _pos.value), _filePos,
"Unexpecting end of parsing exml::internal::Element : '" + nameElement + "' == > check if the '/>' is set or the end of element"));
return false; return false;
} }
@ -269,24 +274,26 @@ public class ParseXml {
return true; return true;
} }
} }
parsingProperty.createError(_data, _pos.value, _filePos, "Text got end of file without finding end node"); parsingProperty.createError(new ExmlParserError(Tools.extractLine(_data, _pos.value), _filePos, "Text got end of file without finding end node"));
_pos.value = _data.length(); _pos.value = _data.length();
return false; return false;
} }
public Object parse(final String data) throws ExmlBuilderException { public Object parse(final String data, final ParsingProperty property) throws ExmlBuilderException, ExmlParserErrorMulti {
Log.verbose("Start parsing document (type: string) size=" + data.length()); Log.verbose("Start parsing document (type: string) size=" + data.length());
// came from char == > force in utf8 ... // came from char == > force in utf8 ...
final FilePos pos = new FilePos(1, 0); final FilePos pos = new FilePos(1, 0);
final PositionParsing parsePos = new PositionParsing(); final PositionParsing parsePos = new PositionParsing();
final ParsingProperty property = new ParsingProperty();
final Object rootNode = this.builder.newRoot(); final Object rootNode = this.builder.newRoot();
if (subParseElement(rootNode, null, data, parsePos, pos, property) == true) { subParseElement(rootNode, null, data, parsePos, pos, property);
return rootNode; if (property.isErrorDetected() == true) {
if (property.isThrowOnError() == true) {
throw new ExmlParserErrorMulti("Parsing error multiple error detected", property.getErrors());
} }
return null; return null;
}
return rootNode;
} }
/** /**
@ -311,14 +318,14 @@ public class ParseXml {
final int white = Tools.countWhiteChar(_data, iii + 1, tmpPos); final int white = Tools.countWhiteChar(_data, iii + 1, tmpPos);
if (iii + white + 1 >= _data.length()) { if (iii + white + 1 >= _data.length()) {
_filePos.add(tmpPos); _filePos.add(tmpPos);
parsingProperty.createError(_data, _pos.value, _filePos, "End file with '<' char == > invalide XML"); parsingProperty.createError(new ExmlParserError(Tools.extractLine(_data, _pos.value), _filePos, "End file with '<' char == > invalide XML"));
_pos.value = iii + white; _pos.value = iii + white;
return false; return false;
} }
// Detect type of the element: // Detect type of the element:
if (_data.charAt(iii + white + 1) == '>') { if (_data.charAt(iii + white + 1) == '>') {
_filePos.add(tmpPos); _filePos.add(tmpPos);
parsingProperty.createError(_data, _pos.value, _filePos, "Find '>' with no element in the element..."); parsingProperty.createError(new ExmlParserError(Tools.extractLine(_data, _pos.value), _filePos, "Find '>' with no element in the element..."));
_pos.value = iii + white + 1; _pos.value = iii + white + 1;
return false; return false;
} }
@ -326,7 +333,7 @@ public class ParseXml {
tmpPos.increment(); tmpPos.increment();
// TODO : white space ... // TODO : white space ...
if (Tools.checkAvaillable(_data.charAt(iii + white + 2), true) == false) { if (Tools.checkAvaillable(_data.charAt(iii + white + 2), true) == false) {
parsingProperty.createError(_data, _pos.value, _filePos, "Find unavaillable name in the Declaration node..."); parsingProperty.createError(new ExmlParserError(Tools.extractLine(_data, _pos.value), _filePos, "Find unavaillable name in the Declaration node..."));
_pos.value = iii + white + 1; _pos.value = iii + white + 1;
return false; return false;
} }
@ -343,8 +350,10 @@ public class ParseXml {
tmpPos.check(_data.charAt(jjj)); tmpPos.check(_data.charAt(jjj));
} }
String tmpname = _data.substring(iii + white + 2, endPosName + 1); String tmpname = _data.substring(iii + white + 2, endPosName + 1);
if (parsingProperty.getCaseSensitive() == true) { if (parsingProperty.getStoreMode() == StoreMode.LOWERCASE) {
tmpname = tmpname.toLowerCase(); tmpname = tmpname.toLowerCase();
} else if (parsingProperty.getStoreMode() == StoreMode.UPPERCASE) {
tmpname = tmpname.toUpperCase();
} }
// Find declaration marker // Find declaration marker
final Object declaration = this.builder.newDeclaration(parent, tmpname); final Object declaration = this.builder.newDeclaration(parent, tmpname);
@ -361,17 +370,18 @@ public class ParseXml {
tmpPos.increment(); tmpPos.increment();
// Find special block element // Find special block element
if (iii + white + 2 >= _data.length()) { if (iii + white + 2 >= _data.length()) {
parsingProperty.createError(_data, _pos.value, _filePos, "End file with '<!' chars == > invalide XML"); parsingProperty.createError(new ExmlParserError(Tools.extractLine(_data, _pos.value), _filePos, "End file with '<!' chars == > invalide XML"));
return false; return false;
} }
if (_data.charAt(iii + white + 2) == '-') { if (_data.charAt(iii + white + 2) == '-') {
tmpPos.increment(); tmpPos.increment();
if (iii + white + 3 >= _data.length()) { if (iii + white + 3 >= _data.length()) {
parsingProperty.createError(_data, _pos.value, _filePos, "End file with '<!-' chars == > invalide XML"); parsingProperty.createError(new ExmlParserError(Tools.extractLine(_data, _pos.value), _filePos, "End file with '<!-' chars == > invalide XML"));
return false; return false;
} }
if (_data.charAt(iii + white + 3) != '-') { if (_data.charAt(iii + white + 3) != '-') {
parsingProperty.createError(_data, _pos.value, _filePos, "Element parse with '<!-" + _data.charAt(iii + 3) + "' chars == > invalide XML"); parsingProperty
.createError(new ExmlParserError(Tools.extractLine(_data, _pos.value), _filePos, "Element parse with '<!-" + _data.charAt(iii + 3) + "' chars == > invalide XML"));
return false; return false;
} }
tmpPos.increment(); tmpPos.increment();
@ -385,13 +395,14 @@ public class ParseXml {
} else if (_data.charAt(iii + white + 2) == '[') { } else if (_data.charAt(iii + white + 2) == '[') {
tmpPos.increment(); tmpPos.increment();
if (iii + white + 8 >= _data.length()) { if (iii + white + 8 >= _data.length()) {
parsingProperty.createError(_data, _pos.value, _filePos, "End file with '<![' chars == > invalide XML"); parsingProperty.createError(new ExmlParserError(Tools.extractLine(_data, _pos.value), _filePos, "End file with '<![' chars == > invalide XML"));
return false; 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' 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) != '[') { || _data.charAt(iii + white + 7) != 'A' || _data.charAt(iii + white + 8) != '[') {
parsingProperty.createError(_data, _pos.value, _filePos, "Element parse with '<![" + _data.charAt(iii + white + 3) + _data.charAt(iii + white + 4) parsingProperty.createError(new ExmlParserError(Tools.extractLine(_data, _pos.value), _filePos,
+ _data.charAt(iii + white + 5) + _data.charAt(iii + white + 6) + _data.charAt(iii + white + 7) + _data.charAt(iii + white + 8) + "' chars == > invalide XML"); "Element parse with '<![" + _data.charAt(iii + white + 3) + _data.charAt(iii + white + 4) + _data.charAt(iii + white + 5) + _data.charAt(iii + white + 6)
+ _data.charAt(iii + white + 7) + _data.charAt(iii + white + 8) + "' chars == > invalide XML"));
return false; return false;
} }
tmpPos.add(6); tmpPos.add(6);
@ -403,7 +414,8 @@ public class ParseXml {
} }
iii = _pos.value; iii = _pos.value;
} else { } else {
parsingProperty.createError(_data, _pos.value, _filePos, "End file with '<!" + _data.charAt(iii + white + 2) + "' chars == > invalide XML"); parsingProperty
.createError(new ExmlParserError(Tools.extractLine(_data, _pos.value), _filePos, "End file with '<!" + _data.charAt(iii + white + 2) + "' chars == > invalide XML"));
return false; return false;
} }
continue; continue;
@ -422,11 +434,14 @@ public class ParseXml {
} }
tmpPos.check(_data.charAt(jjj)); tmpPos.check(_data.charAt(jjj));
} }
String tmpname = _data.substring(iii + white + 2, endPosName + 1); final String tmpname = _data.substring(iii + white + 2, endPosName + 1);
String tmpnameCheck = tmpname;
String nameElementCheck = nameElement;
if (parsingProperty.getCaseSensitive() == true) { if (parsingProperty.getCaseSensitive() == true) {
tmpname = tmpname.toLowerCase(); tmpnameCheck = tmpname.toLowerCase();
nameElementCheck = nameElement.toLowerCase();
} }
if (tmpname.contentEquals(nameElement) == true) { if (tmpnameCheck.contentEquals(nameElementCheck) == true) {
// find end of node : // find end of node :
// find > element ... // find > element ...
for (int jjj = endPosName + 1; jjj < _data.length(); jjj++) { for (int jjj = endPosName + 1; jjj < _data.length(); jjj++) {
@ -440,18 +455,19 @@ public class ParseXml {
return true; return true;
} else if (_data.charAt(jjj) != '\r' && _data.charAt(jjj) != ' ' && _data.charAt(jjj) != '\t') { } else if (_data.charAt(jjj) != '\r' && _data.charAt(jjj) != ' ' && _data.charAt(jjj) != '\t') {
_filePos.add(tmpPos); _filePos.add(tmpPos);
parsingProperty.createError(_data, jjj, _filePos, "End node error : have data inside end node other than [ \\n\\t\\r] " + nameElement + "'"); parsingProperty.createError(
new ExmlParserError(Tools.extractLine(_data, jjj), _filePos, "End node error : have data inside end node other than [ \\n\\t\\r] " + nameElement + "'"));
return false; return false;
} }
} }
} else { } else {
parsingProperty.createError(_data, _pos.value, _filePos, "End node error : '" + tmpname + "' != '" + nameElement + "'"); parsingProperty.createError(new ExmlParserError(Tools.extractLine(_data, _pos.value), _filePos, "End node error : '" + tmpname + "' != '" + nameElement + "'"));
return false; return false;
} }
} }
if (_data.charAt(iii + white + 1) == '>') { if (_data.charAt(iii + white + 1) == '>') {
// end of something == > this is really bad // end of something == > this is really bad
parsingProperty.createError(_data, _pos.value, _filePos, "Find '>' chars == > invalide XML"); parsingProperty.createError(new ExmlParserError(Tools.extractLine(_data, _pos.value), _filePos, "Find '>' chars == > invalide XML"));
return false; return false;
} }
@ -470,16 +486,20 @@ public class ParseXml {
tmpPos.check(_data.charAt(jjj)); tmpPos.check(_data.charAt(jjj));
} }
String tmpname = _data.substring(iii + white + 1, endPosName + 1); String tmpname = _data.substring(iii + white + 1, endPosName + 1);
if (parsingProperty.getCaseSensitive() == true) { final String tmpnameOriginal = tmpname;
if (parsingProperty.getStoreMode() == StoreMode.LOWERCASE) {
tmpname = tmpname.toLowerCase(); tmpname = tmpname.toLowerCase();
} else if (parsingProperty.getStoreMode() == StoreMode.UPPERCASE) {
tmpname = tmpname.toUpperCase();
} }
Log.debug("find node named : '" + tmpname + "'"); Log.debug("find node named : '" + tmpname + "'");
// find text: // find text:
final Object element = this.builder.newElement(parent, tmpname); final Object element = this.builder.newElement(parent, tmpname);
_pos.value = endPosName + 1; _pos.value = endPosName + 1;
_filePos.add(tmpPos); _filePos.add(tmpPos);
Log.verbose("start parse : 'element' named='" + tmpname + "'"); Log.verbose("start parse : 'element' named='" + tmpname + "'");
if (iParseElement(element, tmpname, _data, _pos, _filePos, parsingProperty) == false) { if (iParseElement(element, tmpnameOriginal, _data, _pos, _filePos, parsingProperty) == false) {
return false; return false;
} }
iii = _pos.value; iii = _pos.value;
@ -487,11 +507,11 @@ public class ParseXml {
} }
_filePos.add(tmpPos); _filePos.add(tmpPos);
// here we have an error : // here we have an error :
parsingProperty.createError(_data, _pos.value, _filePos, "Find an ununderstanding element : '" + _data.charAt(iii + white + 1) + "'"); parsingProperty.createError(new ExmlParserError(Tools.extractLine(_data, _pos.value), _filePos, "Find an ununderstanding element : '" + _data.charAt(iii + white + 1) + "'"));
return false; return false;
} else { } else {
if (_data.charAt(iii) == '>') { if (_data.charAt(iii) == '>') {
parsingProperty.createError(_data, _pos.value, _filePos, "Find elemement '>' == > no reason to be here ..."); parsingProperty.createError(new ExmlParserError(Tools.extractLine(_data, _pos.value), _filePos, "Find elemement '>' == > no reason to be here ..."));
return false; return false;
} }
// might to be data text ... // might to be data text ...
@ -511,7 +531,7 @@ public class ParseXml {
if (nameElement == null) { if (nameElement == null) {
return true; return true;
} }
parsingProperty.createError(_data, _pos.value, _filePos, "Did not find end of the exml::internal::Element : '" + nameElement + "'"); parsingProperty.createError(new ExmlParserError(Tools.extractLine(_data, _pos.value), _filePos, "Did not find end of the Element : '" + nameElement + "'"));
return false; return false;
} }
} }

View File

@ -1,54 +1,78 @@
package org.atriasoft.exml.parser; package org.atriasoft.exml.parser;
import org.atriasoft.exml.internal.FilePos; import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.atriasoft.exml.exception.ExmlParserError;
import org.atriasoft.exml.internal.Log; import org.atriasoft.exml.internal.Log;
import org.atriasoft.exml.internal.Tools;
public class ParsingProperty { public class ParsingProperty {
private boolean caseSensitive; //!< check the case sensitive of the nodes and attribute /// check the case sensitive of the nodes (end marker) and attribute (duplicates)
private boolean writeErrorWhenDetexted; //!< Request print error in parsing just when detected private boolean caseSensitive = true;
// Mode to store the Element name or the Attibute name
private String comment; //!< Comment on the error; private StoreMode storeMode = StoreMode.NORMAL;
private String Line; //!< Parse line error (copy); /// write error when not throw on error.
private FilePos filePos; //!< position of the error private boolean writeError = false;
/// throw when an error when it is detected (if permissive XML it throw only at the end of parsing).
private boolean throwOnError = true;
/// Permissive XML parsing (allows some errors must not be critical).
private boolean permisiveXML = false;
// List of all error detected
private final List<ExmlParserError> errors = new ArrayList<>();
/** /**
* Constructor * Constructor
*/ */
public ParsingProperty() { public ParsingProperty() {}
this.caseSensitive = false;
this.writeErrorWhenDetexted = true; /**
this.comment = ""; * Constructor
this.Line = ""; * @param caseSensitive set the parsing of value is case sensitive.
this.filePos = new FilePos(0, 0); * @param writeError Write all error when detected.
* @param throwOnError throw an error when parsing fail (if permissiveXml, it throw only at the end).
* @param permisiveXML Allow error and try to continue the parsing it is possible.
*/
public ParsingProperty(final boolean caseSensitive, final boolean writeError, final boolean throwOnError, final boolean permisiveXML) {
this.caseSensitive = caseSensitive;
this.writeError = writeError;
this.throwOnError = throwOnError;
this.permisiveXML = permisiveXML;
} }
/** /**
* Create an error in the parsing (call by the syetm for error management) * Add an error in the parsing of the XML (the function will select if we need to throw, print or ...)
* @param[in] _data string of chat is wrong * @param error the error to throw or keep.
* @param[in] _pos Position in the file * @return true if the parsing will stop
* @param[in] _filePos human position of the error * @throws ExmlParserError the error injected if user request it.
* @param[in] _comment Error string to display
*/ */
public void createError(final String _data, final int _pos, final FilePos _filePos, final String _comment) { public boolean createError(final ExmlParserError error) throws ExmlParserError {
this.comment = _comment; // need display the error
this.Line = Tools.extract_line(_data, _pos); if (this.writeError == true) {
this.filePos = _filePos; displayError(error);
if (this.writeErrorWhenDetexted == true) {
displayError();
} }
// need throw the error
if (this.throwOnError == true && this.permisiveXML == false) {
throw error;
}
// Keep it in case
this.errors.add(error);
return this.permisiveXML == false;
}
/**
* Request Display in log all the errors.
*/
public void displayError() {
this.errors.forEach(o -> displayError(o));
} }
/** /**
* Request display in log of the error * Request display in log of the error
* @param error The error to display.
*/ */
public void displayError() { public void displayError(final ExmlParserError error) {
if (this.comment.length() == 0) { Log.error(error.getFilePos() + " " + error.getMessage() + "\n" + error.getDataLine() + "\n" + Tools.createPosPointer(error.getDataLine(), error.getFilePos().getCol()));
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");
} }
/** /**
@ -65,14 +89,53 @@ public class ParsingProperty {
* @return false Does not display error (get it at end) * @return false Does not display error (get it at end)
*/ */
public boolean getDisplayError() { public boolean getDisplayError() {
return this.writeErrorWhenDetexted; return this.writeError;
}
/**
* Get all error detected in the XML
* @return Immutable list of error (order by arrival)
*/
public List<ExmlParserError> getErrors() {
return Collections.unmodifiableList(this.errors);
}
/**
* Get the current storing mode
* @return store element and attribute values
*/
public StoreMode getStoreMode() {
return this.storeMode;
}
/**
* Check is error has been detected
* @return true if some error are stored.
*/
public boolean isErrorDetected() {
return this.errors.isEmpty() == false;
}
/**
* Get the permissive value
* @return true if permissive mode is enable
*/
public boolean isPermisiveXML() {
return this.permisiveXML;
}
/**
* Check if throw on error
* @return true if it throw on error
*/
public boolean isThrowOnError() {
return this.throwOnError;
} }
/** /**
* Enable or diasable the case sensitive (must be done before the call of parsing) * Enable or diasable the case sensitive (must be done before the call of parsing)
* @param[in] _val true if enable; false else. * @param[in] _val true if enable; false else.
*/ */
// TODO: Naming error, it is insensitive ...
public void setCaseSensitive(final boolean _val) { public void setCaseSensitive(final boolean _val) {
this.caseSensitive = _val; this.caseSensitive = _val;
} }
@ -82,6 +145,30 @@ public class ParsingProperty {
* @param[in] _value true: display error, false not display error (get it at end) * @param[in] _value true: display error, false not display error (get it at end)
*/ */
public void setDisplayError(final boolean _value) { public void setDisplayError(final boolean _value) {
this.writeErrorWhenDetexted = _value; this.writeError = _value;
}
/**
* Set the permissive value.
* @param permisiveXML new value of permissive parsing.
*/
public void setPermisiveXML(final boolean permisiveXML) {
this.permisiveXML = permisiveXML;
}
/**
* Set the new storing mode for Element and Attributes
* @param storeMode new Storing mode
*/
public void setStoreMode(final StoreMode storeMode) {
this.storeMode = storeMode;
}
/**
* Set throwing on error (if permissive, it throw at the end of parsing)
* @param throwOnError true if it throw on error.
*/
public void setThrowOnError(final boolean throwOnError) {
this.throwOnError = throwOnError;
} }
} }

View File

@ -1,6 +1,5 @@
package org.atriasoft.exml.internal; package org.atriasoft.exml.parser;
public class PositionParsing { public class PositionParsing {
public int value = 0; public int value = 0;
} }

View File

@ -0,0 +1,7 @@
package org.atriasoft.exml.parser;
public enum StoreMode {
NORMAL,
UPPERCASE,
LOWERCASE,
}

View File

@ -1,4 +1,6 @@
package org.atriasoft.exml.internal; package org.atriasoft.exml.parser;
import org.atriasoft.exml.internal.Log;
public class Tools { public class Tools {
/** /**
@ -88,7 +90,7 @@ public class Tools {
} }
} }
public static String extract_line(final String data, final int _pos) { public static String extractLine(final String data, final int _pos) {
// search back : '\n' // search back : '\n'
int startPos = data.lastIndexOf('\n', _pos); int startPos = data.lastIndexOf('\n', _pos);
if (startPos == _pos) { if (startPos == _pos) {

View File

@ -0,0 +1,112 @@
package org.atriasoft.exml.serializer;
import java.util.List;
import org.atriasoft.exml.internal.Log;
import org.atriasoft.exml.model.XmlAttribute;
import org.atriasoft.exml.model.XmlAttributeList;
import org.atriasoft.exml.model.XmlComment;
import org.atriasoft.exml.model.XmlDeclaration;
import org.atriasoft.exml.model.XmlElement;
import org.atriasoft.exml.model.XmlNode;
import org.atriasoft.exml.model.XmlNodeType;
import org.atriasoft.exml.model.XmlText;
import org.atriasoft.exml.parser.Tools;
public class SerializerXml {
public static void serialize(final XmlNode node, final StringBuilder data, final int indent) {
if (node instanceof XmlElement) {
serializeElement((XmlElement) node, data, indent);
} else if (node instanceof XmlText) {
serializeText((XmlText) node, data, indent);
} else if (node instanceof XmlDeclaration) {
serializeDeclaration((XmlDeclaration) node, data, indent);
} else if (node instanceof XmlComment) {
serializeComment((XmlComment) node, data, indent);
} else {
// TODO throw an error ...
}
}
private static void serializeAttribute(final XmlAttribute attribute, final StringBuilder data, final int indent) {
data.append(" ");
data.append(attribute.getName());
data.append("=\"");
data.append(attribute.getValue());
data.append("\"");
}
private static void serializeAttributeList(final XmlAttributeList list, final StringBuilder data, final int indent) {
for (int iii = 0; iii < list.getAttributes().size(); iii++) {
serializeAttribute(list.getAttributes().get(iii), data, indent);
}
}
private static void serializeComment(final XmlComment comment, final StringBuilder data, final int indent) {
Tools.addIndent(data, indent);
data.append("<!--");
data.append(comment.getValue());
data.append("-->\n");
}
private static void serializeDeclaration(final XmlDeclaration declaration, final StringBuilder data, final int indent) {
Tools.addIndent(data, indent);
data.append("<?");
data.append(declaration.getValue());
serializeAttributeList(declaration, data, indent);
data.append("?>\n");
}
private static void serializeElement(final XmlElement element, final StringBuilder data, final int indent) {
Tools.addIndent(data, indent);
data.append("<");
data.append(element.getValue());
serializeAttributeList(element, data, indent);
final List<XmlNode> nodes = element.getNodes();
if (nodes.size() > 0) {
if (nodes.size() == 1 && nodes.get(0) != null && nodes.get(0).getType() == XmlNodeType.TEXT && ((XmlText) nodes.get(0)).countLines() == 1) {
data.append(">");
serialize(nodes.get(0), data, 0);
Log.verbose(" generate : '" + data + "'");
} else {
data.append(">\n");
for (int iii = 0; iii < nodes.size(); iii++) {
if (nodes.get(iii) != null) {
serialize(nodes.get(iii), data, indent + 1);
}
}
Tools.addIndent(data, indent);
}
data.append("</");
data.append(element.getValue());
data.append(">\n");
} else {
data.append("/>\n");
}
}
public static void serializeRoot(final XmlElement root, final StringBuilder data) {
for (int iii = 0; iii < root.getNodes().size(); iii++) {
final XmlNode node = root.getNodes().get(iii);
serialize(node, data, 0);
}
}
private static void serializeText(final XmlText text, final StringBuilder data, final int indent) {
data.append(Tools.replaceSpecialCharOut(text.getValue()));
}
/* CDATA generation ... maybe to add later ...
@Override
protected boolean iGenerate(final StringBuilder _data, final int _indent) {
_data.append("<![CDATA[");
_data.append(this.value);
_data.append("]]>");
return true;
}
*/
}

View File

@ -5,8 +5,9 @@
*/ */
package test.atriasoft.exml; package test.atriasoft.exml;
import org.atriasoft.exml.Document;
import org.atriasoft.exml.Exml; import org.atriasoft.exml.Exml;
import org.atriasoft.exml.exception.ExmlBuilderException;
import org.atriasoft.exml.model.XmlNode;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
class ExmlLocal { class ExmlLocal {
@ -16,24 +17,25 @@ class ExmlLocal {
} }
public static void test(final String _ref, final String _input, final int _errorPos, final boolean _caseInSensitive) { public static void test(final String _ref, final String _input, final int _errorPos, final boolean _caseInSensitive) {
final Document doc = new Document();
//doc.setCaseSensitive(!_caseInSensitive); //doc.setCaseSensitive(!_caseInSensitive);
Log.verbose("parse : \n" + _input); Log.verbose("parse : \n" + _input);
final boolean retParse = Exml.parse(_input); XmlNode root = null;
try {
root = Exml.parse(_input);
if (_errorPos == 1) {
Assertions.fail("Must have detected an error");
return;
}
} catch (final ExmlBuilderException e) {
if (_errorPos == 1) { if (_errorPos == 1) {
Assertions.assertEquals(retParse, false);
return; return;
} else { } else {
Assertions.assertEquals(retParse, true); Assertions.fail("Must have NOT detected an error");
}
} }
final StringBuilder out = new StringBuilder(); final StringBuilder out = new StringBuilder();
final boolean retGenerate = doc.generate(out); // TODO: 2 is for failing in generate ...
if (_errorPos == 2) { Exml.generate(root, out);
Assertions.assertEquals(retGenerate, false);
return;
} else {
Assertions.assertEquals(retGenerate, true);
}
final String data = out.toString(); final String data = out.toString();
if (_errorPos == 3) { if (_errorPos == 3) {
Assertions.assertNotEquals(_ref, data); Assertions.assertNotEquals(_ref, data);

View File

@ -5,11 +5,10 @@
*/ */
package test.atriasoft.exml; package test.atriasoft.exml;
import org.atriasoft.exml.XmlAttribute; import org.atriasoft.exml.Exml;
import org.atriasoft.exml.Document;
import org.atriasoft.exml.XmlElement;
import org.atriasoft.exml.exception.ExmlAttributeDoesNotExist; import org.atriasoft.exml.exception.ExmlAttributeDoesNotExist;
import org.atriasoft.exml.exception.ExmlNodeDoesNotExist; import org.atriasoft.exml.model.XmlAttribute;
import org.atriasoft.exml.model.XmlElement;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -28,7 +27,6 @@ public class ExmlTestAttribute {
@Test @Test
public void clear() { public void clear() {
final XmlAttribute myAttribute = new XmlAttribute("nameAttribute", "valueAttribute"); final XmlAttribute myAttribute = new XmlAttribute("nameAttribute", "valueAttribute");
myAttribute.clear(); myAttribute.clear();
} }
@ -51,7 +49,6 @@ public class ExmlTestAttribute {
@Test @Test
public void exist() { public void exist() {
final XmlElement elem = new XmlElement("elem"); final XmlElement elem = new XmlElement("elem");
elem.setAttribute("valA", "plop"); elem.setAttribute("valA", "plop");
Assertions.assertEquals(elem.existAttribute("valA"), true); Assertions.assertEquals(elem.existAttribute("valA"), true);
@ -60,43 +57,29 @@ public class ExmlTestAttribute {
@Test @Test
public void get() { public void get() {
final XmlElement elem = new XmlElement("elem"); final XmlElement elem = new XmlElement("elem");
elem.setAttribute("valA", "plop"); elem.setAttribute("valA", "plop");
try { final String attributeValue = Assertions.assertDoesNotThrow(() -> elem.getAttribute("valA"));
Assertions.assertEquals(elem.getAttribute("valA"), "plop"); Assertions.assertEquals(attributeValue, "plop");
} catch (final ExmlAttributeDoesNotExist e) {
Assertions.fail("Should Not have thrown an exception");
}
Assertions.assertThrows(ExmlAttributeDoesNotExist.class, () -> elem.getAttribute("qsdfsdf")); Assertions.assertThrows(ExmlAttributeDoesNotExist.class, () -> elem.getAttribute("qsdfsdf"));
} }
@Test @Test
public void getpair() { public void getpair() {
final XmlElement elem = new XmlElement("elem"); final XmlElement elem = new XmlElement("elem");
elem.setAttribute("valA", "coucou"); elem.setAttribute("valA", "coucou");
try { final String first = Assertions.assertDoesNotThrow(() -> elem.getAttrPair(0).first);
Assertions.assertEquals(elem.getAttrPair(0).first, "valA"); Assertions.assertEquals(first, "valA");
Assertions.assertEquals(elem.getAttrPair(0).second, "coucou"); final String second = Assertions.assertDoesNotThrow(() -> elem.getAttrPair(0).second);
} catch (final ExmlAttributeDoesNotExist e) { Assertions.assertEquals(second, "coucou");
Assertions.fail("Should Not have thrown an exception");
}
Assertions.assertThrows(ExmlAttributeDoesNotExist.class, () -> elem.getAttrPair(1)); Assertions.assertThrows(ExmlAttributeDoesNotExist.class, () -> elem.getAttrPair(1));
Assertions.assertThrows(ExmlAttributeDoesNotExist.class, () -> elem.getAttrPair(-1)); Assertions.assertThrows(ExmlAttributeDoesNotExist.class, () -> elem.getAttrPair(-1));
} }
@Test @Test
public void moveInAllElement() { public void moveInAllElement() {
final Document doc = new Document(); final XmlElement root = Assertions.assertDoesNotThrow(() -> Exml.parse("<elem valA=\"plop\"/>"));
doc.parse("<elem valA=\"plop\"/>"); final XmlElement elem = Assertions.assertDoesNotThrow(() -> (XmlElement) root.getNode("elem"));
XmlElement elem;
try {
elem = (XmlElement) doc.getNode("elem");
} catch (final ExmlNodeDoesNotExist e) {
Assertions.fail("Should Not have thrown an exception");
return;
}
for (final XmlAttribute it : elem.getAttributes()) { for (final XmlAttribute it : elem.getAttributes()) {
Assertions.assertEquals(it.getName(), "valA"); Assertions.assertEquals(it.getName(), "valA");
Assertions.assertEquals(it.getValue(), "plop"); Assertions.assertEquals(it.getValue(), "plop");
@ -114,7 +97,6 @@ public class ExmlTestAttribute {
@Test @Test
public void setGetName() { public void setGetName() {
final XmlAttribute myAttribute = new XmlAttribute("nameAttribute", "valueAttribute"); final XmlAttribute myAttribute = new XmlAttribute("nameAttribute", "valueAttribute");
Assertions.assertEquals(myAttribute.getName(), "nameAttribute"); Assertions.assertEquals(myAttribute.getName(), "nameAttribute");
myAttribute.setName("newName"); myAttribute.setName("newName");
@ -123,7 +105,6 @@ public class ExmlTestAttribute {
@Test @Test
public void setGetValue() { public void setGetValue() {
final XmlAttribute myAttribute = new XmlAttribute("nameAttribute", "valueAttribute"); final XmlAttribute myAttribute = new XmlAttribute("nameAttribute", "valueAttribute");
Assertions.assertEquals(myAttribute.getValue(), "valueAttribute"); Assertions.assertEquals(myAttribute.getValue(), "valueAttribute");
myAttribute.setValue("new value"); myAttribute.setValue("new value");
@ -132,31 +113,20 @@ public class ExmlTestAttribute {
@Test @Test
public void setterNew() { public void setterNew() {
final XmlElement elem = new XmlElement("elem"); final XmlElement elem = new XmlElement("elem");
elem.setAttribute("valA", "coucou"); elem.setAttribute("valA", "coucou");
try { final String attributeValue = Assertions.assertDoesNotThrow(() -> elem.getAttribute("valA"));
Assertions.assertEquals(elem.getAttribute("valA"), "coucou"); Assertions.assertEquals(attributeValue, "coucou");
} catch (final ExmlAttributeDoesNotExist e) {
Assertions.fail("Should Not have thrown an exception");
}
} }
@Test @Test
public void setterRewrite() { public void setterRewrite() {
final XmlElement elem = new XmlElement("elem"); final XmlElement elem = new XmlElement("elem");
elem.setAttribute("valA", "coucou"); elem.setAttribute("valA", "coucou");
try { String attributeValue = Assertions.assertDoesNotThrow(() -> elem.getAttribute("valA"));
Assertions.assertEquals(elem.getAttribute("valA"), "coucou"); Assertions.assertEquals(attributeValue, "coucou");
} catch (final ExmlAttributeDoesNotExist e) {
Assertions.fail("Should Not have thrown an exception");
}
elem.setAttribute("valA", "coucou2"); elem.setAttribute("valA", "coucou2");
try { attributeValue = Assertions.assertDoesNotThrow(() -> elem.getAttribute("valA"));
Assertions.assertEquals(elem.getAttribute("valA"), "coucou2"); Assertions.assertEquals(attributeValue, "coucou2");
} catch (final ExmlAttributeDoesNotExist e) {
Assertions.fail("Should Not have thrown an exception");
}
} }
} }

View File

@ -5,9 +5,8 @@
*/ */
package test.atriasoft.exml; package test.atriasoft.exml;
import org.atriasoft.exml.Document; import org.atriasoft.exml.Exml;
import org.atriasoft.exml.XmlElement; import org.atriasoft.exml.model.XmlElement;
import org.atriasoft.exml.TextCDATA;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -18,23 +17,10 @@ public class ExmlTestCData {
Log.verbose("----------------------------------------------------------------"); Log.verbose("----------------------------------------------------------------");
} }
@Test
public void generate() {
final Document doc = new Document();
doc.append(new TextCDATA("Text &é<>examp]le]] ..."));
doc.append(new TextCDATA("Text &é<>examp]le]] ..."));
final String out = doc.getText();
doc.parse("<elem><![CDATA[Text &é<>examp]le]] ...]]></elem>");
Assertions.assertEquals("<![CDATA[Text &é<>examp]le]] ...]]><![CDATA[Text &é<>examp]le]] ...]]>", out);
}
@Test @Test
public void parseCDATA() { public void parseCDATA() {
final Document doc = new Document(); final XmlElement root = Assertions.assertDoesNotThrow(() -> Exml.parse("<elem><![CDATA[Text &é<>examp]le]] ...]]></elem>"));
doc.parse("<elem><![CDATA[Text &é<>examp]le]] ...]]></elem>"); final XmlElement elem = Assertions.assertDoesNotThrow(() -> (XmlElement) root.getNode("elem"));
final XmlElement elem = Assertions.assertDoesNotThrow(() -> {
return (XmlElement) doc.getNode("elem");
});
Assertions.assertEquals("Text &é<>examp]le]] ...", elem.getText()); Assertions.assertEquals("Text &é<>examp]le]] ...", elem.getText());
} }
} }

View File

@ -5,9 +5,9 @@
*/ */
package test.atriasoft.exml; package test.atriasoft.exml;
import org.atriasoft.exml.XmlComment; import org.atriasoft.exml.model.XmlComment;
import org.atriasoft.exml.XmlNode; import org.atriasoft.exml.model.XmlNode;
import org.atriasoft.exml.XmlNodeType; import org.atriasoft.exml.model.XmlNodeType;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;

View File

@ -5,9 +5,9 @@
*/ */
package test.atriasoft.exml; package test.atriasoft.exml;
import org.atriasoft.exml.DeclarationXML;
import org.atriasoft.exml.XmlNodeType;
import org.atriasoft.exml.exception.ExmlAttributeDoesNotExist; import org.atriasoft.exml.exception.ExmlAttributeDoesNotExist;
import org.atriasoft.exml.model.XmlDeclaration;
import org.atriasoft.exml.model.XmlNodeType;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -20,7 +20,7 @@ public class ExmlTestDeclarationXML {
@Test @Test
public void create() { public void create() {
final DeclarationXML myDeclarationXML = new DeclarationXML("1.0", "UTF-8", true); final XmlDeclaration myDeclarationXML = new XmlDeclaration("1.0", "UTF-8", true);
Assertions.assertEquals(myDeclarationXML.getType(), XmlNodeType.DECLARATION); Assertions.assertEquals(myDeclarationXML.getType(), XmlNodeType.DECLARATION);
try { try {
Assertions.assertEquals(myDeclarationXML.getAttribute("version"), "1.0"); Assertions.assertEquals(myDeclarationXML.getAttribute("version"), "1.0");
@ -34,8 +34,8 @@ public class ExmlTestDeclarationXML {
@Test @Test
public void createAssignement() { public void createAssignement() {
final DeclarationXML myDeclarationXML = new DeclarationXML("1.0", "UTF-8", true); final XmlDeclaration myDeclarationXML = new XmlDeclaration("1.0", "UTF-8", true);
final DeclarationXML myOtherDeclarationXML = myDeclarationXML.clone(); final XmlDeclaration myOtherDeclarationXML = myDeclarationXML.clone();
Assertions.assertEquals(myDeclarationXML.getValue(), myOtherDeclarationXML.getValue()); Assertions.assertEquals(myDeclarationXML.getValue(), myOtherDeclarationXML.getValue());
try { try {
Assertions.assertEquals(myDeclarationXML.getAttribute("version"), myOtherDeclarationXML.getAttribute("version")); Assertions.assertEquals(myDeclarationXML.getAttribute("version"), myOtherDeclarationXML.getAttribute("version"));
@ -48,8 +48,8 @@ public class ExmlTestDeclarationXML {
@Test @Test
public void createCopy() { public void createCopy() {
final DeclarationXML myDeclarationXML = new DeclarationXML("1.0", "UTF-8", true); final XmlDeclaration myDeclarationXML = new XmlDeclaration("1.0", "UTF-8", true);
final DeclarationXML myOtherDeclarationXML = new DeclarationXML(myDeclarationXML); final XmlDeclaration myOtherDeclarationXML = new XmlDeclaration(myDeclarationXML);
Assertions.assertEquals(myDeclarationXML.getValue(), myOtherDeclarationXML.getValue()); Assertions.assertEquals(myDeclarationXML.getValue(), myOtherDeclarationXML.getValue());
try { try {
Assertions.assertEquals(myDeclarationXML.getAttribute("version"), myOtherDeclarationXML.getAttribute("version")); Assertions.assertEquals(myDeclarationXML.getAttribute("version"), myOtherDeclarationXML.getAttribute("version"));

View File

@ -5,11 +5,11 @@
*/ */
package test.atriasoft.exml; package test.atriasoft.exml;
import org.atriasoft.exml.Document; import org.atriasoft.exml.Exml;
import org.atriasoft.exml.XmlElement;
import org.atriasoft.exml.XmlNode;
import org.atriasoft.exml.XmlNodeType;
import org.atriasoft.exml.exception.ExmlNodeDoesNotExist; import org.atriasoft.exml.exception.ExmlNodeDoesNotExist;
import org.atriasoft.exml.model.XmlElement;
import org.atriasoft.exml.model.XmlNode;
import org.atriasoft.exml.model.XmlNodeType;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -114,9 +114,8 @@ public class ExmlTestElement {
@Test @Test
public void moveInAllElement() { public void moveInAllElement() {
final Document doc = new Document(); final XmlElement root = Assertions.assertDoesNotThrow(() -> Exml.parse("<elem><elem1/><elem2/></elem>"));
doc.parse("<elem><elem1/><elem2/></elem>"); for (final XmlNode it : root.getNodes()) {
for (final XmlNode it : doc.getNodes()) {
final XmlElement elem = (XmlElement) it; final XmlElement elem = (XmlElement) it;
Assertions.assertEquals("elem", elem.getValue()); Assertions.assertEquals("elem", elem.getValue());
Assertions.assertEquals(2, elem.getNodes().size()); Assertions.assertEquals(2, elem.getNodes().size());

View File

@ -29,6 +29,11 @@ public class ExmlTestParseDeclaration {
ExmlLocal.test("<?testDeclaration?>\n", "<?testDeclaration?>\n", -1); ExmlLocal.test("<?testDeclaration?>\n", "<?testDeclaration?>\n", -1);
} }
@Test
public void testCaseSensitiveBalise() {
ExmlLocal.test("<Elem/>\n", "<Elem></elem>\n", -1);
}
@Test @Test
public void testMultiline() { public void testMultiline() {
ExmlLocal.test("<?xml attr=\"plop\"?>\n", "<?xml attr\n=\n\"plop\"?>\n", -1); ExmlLocal.test("<?xml attr=\"plop\"?>\n", "<?xml attr\n=\n\"plop\"?>\n", -1);