
hu.akarnokd.xml.XNElement Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of akarnokd-xml Show documentation
Show all versions of akarnokd-xml Show documentation
akarnokd-xml developed by David Karnok
The newest version!
/*
* Copyright 2015 David Karnok
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
* the License for the specific language governing permissions and limitations under the License.
*/
package hu.akarnokd.xml;
import java.io.*;
import java.net.URL;
import java.sql.*;
import java.util.*;
import java.util.Date;
import java.util.function.Consumer;
import java.util.zip.GZIPInputStream;
import javax.xml.stream.*;
/**
* A simplified XML element model with namespace support.
*/
public class XNElement extends XElementBase {
/** The namespace:name record. */
public static class XAttributeName {
/** The hash. */
private int hash;
/** The name. */
public final String name;
/** The namespace. */
public final String namespace;
/** The namespace prefix. */
public final String prefix;
/**
* Construct an XName entity.
* @param name the name
* @param namespace the namespace
* @param prefix the namespace prefix
*/
public XAttributeName(String name, String namespace, String prefix) {
this.name = name;
this.namespace = namespace;
this.prefix = prefix;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof XAttributeName) {
XAttributeName that = (XAttributeName)obj;
return Objects.equals(this.name, that.name) && Objects.equals(this.namespace, that.namespace);
}
return false;
}
@Override
public int hashCode() {
if (hash == 0) {
hash = Objects.hash(name, namespace);
}
return hash;
}
@Override
public String toString() {
return "{" + namespace + "}" + name;
}
}
/**
* The representation record.
*/
public static class XRepresentationRecord {
/** The offset. */
public final int charOffset;
/** The current element. */
public final XNElement element;
/** The state. */
public final XRepresentationState state;
/**
* Create the record.
* @param state the state
* @param element the element
* @param charOffset the offset
*/
public XRepresentationRecord(XRepresentationState state,
XNElement element, int charOffset) {
super();
this.state = state;
this.element = element;
this.charOffset = charOffset;
}
}
/**
* The representation state.
* @author akarnokd, 2011.10.21.
*/
public enum XRepresentationState {
/** Element end. */
END_ELEMENT,
/** Text end. */
END_TEXT,
/** Element start. */
START_ELEMENT,
/** Text start. */
START_TEXT
}
/**
* Parse an XML from the binary data.
* @param data the XML data
* @return the parsed xml
* @throws XMLStreamException on error
*/
public static XNElement parseXML(byte[] data) throws XMLStreamException {
return parseXML(new ByteArrayInputStream(data));
}
/**
* Parse an XML from the given file.
* @param file the file
* @return the parsed XElement tree
* @throws XMLStreamException if an error occurs
* @throws IOException if the file could not be found or other I/O error occurs
*/
public static XNElement parseXML(File file) throws IOException, XMLStreamException {
try (InputStream in = new FileInputStream(file)) {
return parseXML(in);
}
}
/**
* Parse XML from the input stream. Does not close the stream.
* @param in the InputStream object
* @return the parsed XElement tree
* @throws XMLStreamException if an error occurs
*/
public static XNElement parseXML(InputStream in) throws XMLStreamException {
XMLInputFactory inf = XMLInputFactory.newInstance();
XMLStreamReader ir = inf.createXMLStreamReader(in);
return parseXML(ir);
}
/**
* Parse an XML from the given Reader.
* @param in the Reader
* @return the parsed XElement tree
* @throws XMLStreamException if an error occurs
*/
public static XNElement parseXML(Reader in) throws XMLStreamException {
XMLInputFactory inf = XMLInputFactory.newInstance();
XMLStreamReader ir = inf.createXMLStreamReader(in);
return parseXML(ir);
}
/**
* Reads the contents of a indexed column as an XML.
* @param rs the result set to read from
* @param index the column index
* @return the parsed XNElement or null if the column contained null
* @throws SQLException on SQL error
* @throws IOException on IO error
* @throws XMLStreamException on parsing error
*/
public static XNElement parseXML(ResultSet rs, int index)
throws SQLException, IOException, XMLStreamException {
try (InputStream is = rs.getBinaryStream(index)) {
if (is != null) {
return parseXML(is);
}
return null;
}
}
/**
* Reads the contents of a named column as an XML.
* @param rs the result set to read from
* @param column the column name
* @return the parsed XNElement or null if the column contained null
* @throws SQLException on SQL error
* @throws IOException on IO error
* @throws XMLStreamException on parsing error
*/
public static XNElement parseXML(ResultSet rs, String column)
throws SQLException, IOException, XMLStreamException {
try (InputStream is = rs.getBinaryStream(column)) {
if (is != null) {
return parseXML(is);
}
return null;
}
}
/**
* Parse an XML from the given file.
* @param fileName the file name
* @return the parsed XElement tree
* @throws XMLStreamException if an error occurs
* @throws IOException if the file could not be found or other I/O error occurs
*/
public static XNElement parseXML(String fileName) throws IOException, XMLStreamException {
try (InputStream in = new FileInputStream(fileName)) {
return parseXML(in);
}
}
/**
* Parse an XML from the supplied URL.
* @param url the target URL
* @return the parsed XML
* @throws IOException if an I/O error occurs
* @throws XMLStreamException if a parser error occurs
*/
public static XNElement parseXML(URL url) throws IOException, XMLStreamException {
try (InputStream in = new BufferedInputStream(url.openStream())) {
return parseXML(in);
}
}
/**
* Parse an XML from the given XML Stream reader.
* Closes the {@code in} stream.
* @param in the XMLStreamReader
* @return the parsed XElement tree
* @throws XMLStreamException if an error occurs
*/
public static XNElement parseXML(XMLStreamReader in) throws XMLStreamException {
XNElement root = parseXMLFragment(in);
in.close();
return root;
}
/**
* Parses the stream until the end element of the start element is reached, then
* returns. The method can be used to parse streamed fragment XML on a per node basis.
* It does not close the {@code in} reader.
* @param in the input reader
* @return the parsed XElement tree
* @throws XMLStreamException if an error occurs
*/
public static XNElement parseXMLFragment(XMLStreamReader in)
throws XMLStreamException {
XNElement node = null;
XNElement root = null;
final StringBuilder emptyBuilder = new StringBuilder();
StringBuilder b = null;
Deque stack = new LinkedList<>();
while (in.hasNext()) {
int type = in.next();
switch(type) {
case XMLStreamConstants.START_ELEMENT:
if (b != null) {
stack.push(b);
b = null;
} else {
stack.push(emptyBuilder);
}
XNElement n = new XNElement(in.getName().getLocalPart());
n.namespace = in.getNamespaceURI();
n.prefix = in.getPrefix();
n.parent = node;
int attCount = in.getAttributeCount();
if (attCount > 0) {
for (int i = 0; i < attCount; i++) {
n.attributes.put(new XAttributeName(
in.getAttributeLocalName(i),
in.getAttributeNamespace(i),
in.getAttributePrefix(i)
), in.getAttributeValue(i));
}
}
if (node != null) {
node.children.add(n);
}
node = n;
if (root == null) {
root = n;
}
break;
case XMLStreamConstants.CDATA:
case XMLStreamConstants.CHARACTERS:
if (node != null && !in.isWhiteSpace()) {
if (b == null) {
b = new StringBuilder();
}
b.append(in.getText());
}
break;
case XMLStreamConstants.END_ELEMENT:
// if text was found, assign the accumulated text to the node
if (b != null) {
node.content = b.toString();
}
if (node != null) {
node = node.parent;
}
// return to the parent's builder
b = stack.pop();
// if the parent had no text so far
if (b == emptyBuilder) {
b = null;
}
if (stack.isEmpty()) {
return root;
}
break;
default:
// ignore others.
}
}
return root;
}
/**
* Parse an XML file compressed by GZIP.
* @param file the file
* @return the parsed XML
* @throws XMLStreamException on error
*/
public static XNElement parseXMLGZ(File file) throws XMLStreamException {
try (GZIPInputStream gin = new GZIPInputStream(new BufferedInputStream(new FileInputStream(file), 64 * 1024))) {
return parseXML(gin);
} catch (IOException ex) {
throw new XMLStreamException(ex);
}
}
/**
* Parse an XML file compressed by GZIP.
* @param fileName the filename
* @return the parsed XML
* @throws XMLStreamException on error
*/
public static XNElement parseXMLGZ(String fileName) throws XMLStreamException {
return parseXMLGZ(new File(fileName));
}
/** The attribute map. */
protected final Map attributes = new LinkedHashMap<>();
/** The child elements. */
protected final List children = new ArrayList<>();
/** The optional associated namespace uri. */
public String namespace;
/** The parent element. */
public XNElement parent;
/** The prefix used by this element. */
public String prefix;
/**
* Constructor. Sets the name.
* @param name the element name
*/
public XNElement(String name) {
super(name);
}
/**
* Creates a new XElement and sets its content according to the supplied value.
* @param name the element name
* @param value the content value
*/
public XNElement(String name, Object value) {
this(name);
setValue(value);
}
/**
* Constructor. Sets the name and namespace.
* @param name the element name
* @param namespace the element namespace
*/
public XNElement(String name, String namespace) {
super(name);
this.namespace = namespace;
}
/**
* Creates a new XElement and sets its content according to the supplied value.
* @param name the element name
* @param namespace the namespace
* @param value the content value
*/
public XNElement(String name, String namespace, Object value) {
this(name, namespace);
setValue(value);
}
/**
* Add a new XElement with the given local name and no namespace.
* @param name the name of the new element
* @return the created XElement child
*/
public XNElement add(String name) {
XNElement e = new XNElement(name);
e.parent = this;
children.add(e);
return e;
}
/**
* Add a new XElement with the given local name and namespace.
* @param name the name of the new element
* @param namespace the namespace of the new element
* @return the created XElement child
*/
public XNElement add(String name, String namespace) {
XNElement e = new XNElement(name);
e.namespace = namespace;
e.parent = this;
children.add(e);
return e;
}
/**
* Add the given existing child to this element.
* @param child the child element
* @return the child element
*/
public XNElement add(XNElement child) {
child.parent = this;
children.add(child);
return child;
}
/**
* @return the attribute map
*/
public Map attributes() {
return attributes;
}
/**
* Returns the first child element with the given name.
* @param name the child name
* @return the XElement or null if not present
*/
public XNElement childElement(String name) {
for (XNElement e : children) {
if (e.name.equals(name)) {
return e;
}
}
return null;
}
/**
* Returns the first child element with the given name.
* @param name the child name
* @param namespace the namespace
* @return the XElement or null if not present
*/
public XNElement childElement(String name, String namespace) {
for (XNElement e : children) {
if (Objects.equals(e.name, name) && Objects.equals(e.namespace, namespace)) {
return e;
}
}
return null;
}
/** @return the iterable for all children. */
public List children() {
return children;
}
/*
@Override
public Iterator iterator() {
return children.iterator();
}
*/
/**
* Returns an iterator which enumerates all children with the given name.
* @param name the name of the children to select
* @return the iterator
*/
public List childrenWithName(final String name) {
List result = new ArrayList<>();
for (XNElement c : children) {
if (Objects.equals(c.name, name)) {
result.add(c);
}
}
return result;
}
/**
* Returns an iterator which enumerates all children with the given name.
* @param name the name of the children to select
* @param namespace the namespace URI
* @return the iterator
*/
public List childrenWithName(final String name, final String namespace) {
List result = new ArrayList<>();
for (XNElement c : children) {
if (Objects.equals(c.name, name) && Objects.equals(c.namespace, namespace)) {
result.add(c);
}
}
return result;
}
@Override
public String childValue(String name) {
for (XNElement e : children) {
if (e.name.equals(name)) {
return e.content;
}
}
return null;
}
/**
* Returns the content of the first child which has the given name.
* @param name the child name
* @param namespace the namespace URI
* @return the content or null if no such child
*/
public String childValue(String name, String namespace) {
for (XNElement e : children) {
if (Objects.equals(e.name, name) && Objects.equals(e.namespace, namespace)) {
return e.content;
}
}
return null;
}
@Override
public XNElement copy() {
XNElement e = new XNElement(name);
e.namespace = namespace;
e.prefix = prefix;
e.copyFrom(this);
return e;
}
/**
* Copy the attributes and child elements from the other element.
* @param other the other element
*/
public void copyFrom(XNElement other) {
content = other.content;
userObject = other.userObject;
for (Map.Entry me : other.attributes.entrySet()) {
XAttributeName an = new XAttributeName(me.getKey().name, me.getKey().namespace, me.getKey().prefix);
attributes.put(an, me.getValue());
}
for (XNElement c : children) {
XNElement c0 = c.copy();
c0.parent = this;
children.add(c0);
}
}
/** Simple pair class. */
static final class Pair {
public final T first;
public final U second;
private Pair(T first, U second) {
this.first = first;
this.second = second;
}
public static Pair of(T first, U second) {
return new Pair<>(first, second);
}
@Override
public boolean equals(Object o) {
if (o instanceof Pair) {
Pair, ?> pair = (Pair, ?>) o;
return Objects.equals(first, pair.first)
&& Objects.equals(second, pair.second);
}
return false;
}
@Override
public int hashCode() {
return Objects.hashCode(first) * 31 + Objects.hashCode(second);
}
}
/**
* Generate a new prefix if the current is empty or already exists but with a different URI.
* @param nss the namespace to prefix cache.
* @param currentNamespace the current namespace
* @param currentPrefix the current prefix
* @return a pair of the derived prefix and (the xmlns declaration if needed, null otherwise)
*/
protected Pair createPrefix(Map nss, String currentNamespace, String currentPrefix) {
if (currentNamespace == null) {
return Pair.of(null, null);
}
String pf = nss.get(currentNamespace);
if (pf != null) {
return Pair.of(pf, null);
}
int nsc = 0;
pf = currentPrefix;
if (pf == null) {
pf = "ns" + nsc;
}
outer:
while (!Thread.currentThread().isInterrupted()) {
for (Map.Entry e : nss.entrySet()) {
if (Objects.equals(e.getValue(), pf)) {
nsc++;
pf = "ns" + nsc;
continue outer;
}
}
// if we get here, the prefix is not yet used
break;
}
nss.put(currentNamespace, pf);
StringBuilder xmlns = new StringBuilder();
xmlns.append(" xmlns").append(pf != null && pf.length() > 0 ? ":" : "")
.append(pf != null && pf.length() > 0 ? pf : "").append("='")
.append(sanitize(currentNamespace)).append("'");
;
return Pair.of(pf, xmlns.toString());
}
/** Breaks the link with its parent XElement if any. */
public void detach() {
if (parent != null) {
parent.children.remove(this);
parent = null;
}
}
/**
* Returns a double value of the supplied child or throws an exception if missing.
* @param name the child element name
* @param namespace the element namespace
* @return the value
*/
public double doubleValue(String name, String namespace) {
String s = childValue(name, namespace);
if (s != null) {
return Double.parseDouble(s);
}
throw new IllegalArgumentException(this + ": content: " + name);
}
/**
* Returns a double value or the default value if the element is missing or empty.
* @param name the element name
* @param namespace the element namespace
* @param defaultValue the default value
* @return the value
*/
public double doubleValue(String name, String namespace, double defaultValue) {
String s = childValue(name, namespace);
if (s != null) {
return Double.parseDouble(s);
}
return defaultValue;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof XNElement)) {
return false;
}
XNElement other = (XNElement)obj;
if (!Objects.equals(this.content, other.content)) {
return false;
}
if (!this.attributes.equals(other.attributes)) {
return false;
}
if (!this.children.equals(other.children)) {
return false;
}
return true;
}
/**
* Retrieve the currently associated user object.
* @param the expected object type
* @return the user object
*/
@SuppressWarnings("unchecked")
public T get() {
return (T)userObject;
}
/**
* Retrieve the first attribute which has the given local attribute name.
* @param attributeName the attribute name
* @return the attribute value or null if no such attribute
*/
public String get(String attributeName) {
// check first for a namespace-less attribute
String attr = attributes.get(new XAttributeName(attributeName, null, null));
if (attr == null) {
// find any attribute ignoring namespace
for (XAttributeName n : attributes.keySet()) {
if (Objects.equals(n.name, attributeName)) {
return attributes.get(n);
}
}
}
return attr;
}
/**
* Retrieve the specific attribute.
* @param attributeName the attribute name
* @param attributeNamespace the attribute namespace URI
* @return the attribute value or null if not present
*/
public String get(String attributeName, String attributeNamespace) {
return attributes.get(new XAttributeName(attributeName, attributeNamespace, null));
}
/**
* Retrieve the attribute names.
* @return the list of attribute names
*/
public List getAttributeNames() {
return new ArrayList<>(attributes.keySet());
}
/**
* Retrieve a value of a boolean attribute.
* Throws IllegalArgumentException if the attribute is missing or not of type boolean.
* @param attribute the attribute name
* @return the value
*/
public boolean getBoolean(String attribute) {
String s = get(attribute);
if ("true".equals(s) || "1".equals(s)) {
return true;
} else
if ("false".equals(s) || "0".equals(s)) {
return false;
}
throw new IllegalArgumentException("Attribute " + attribute + " is non-boolean");
}
/**
* Retrieve a value of a boolean attribute.
* @param attribute the attribute name
* @param defaultValue the value to return if no such attribute
* @return the value
*/
public boolean getBoolean(String attribute, boolean defaultValue) {
String s = get(attribute);
if ("true".equals(s) || "1".equals(s)) {
return true;
} else
if ("false".equals(s) || "0".equals(s)) {
return false;
}
return defaultValue;
}
/**
* Retrieve a value of a boolean attribute.
* Throws IllegalArgumentException if the attribute is missing or not of type boolean.
* @param attribute the attribute name
* @param namespace the attribute namespace
* @return the value
*/
public boolean getBoolean(String attribute, String namespace) {
String s = get(attribute, namespace);
if ("true".equals(s) || "1".equals(s)) {
return true;
} else
if ("false".equals(s) || "0".equals(s)) {
return false;
}
throw new IllegalArgumentException("Attribute " + attribute + " is non-boolean");
}
/**
* Retrieve a value of a boolean attribute.
* Throws IllegalArgumentException if the attribute is missing or not of type boolean.
* @param attribute the attribute name
* @param namespace the attribute namespace
* @param defaultValue the value to return if no such attribute
* @return the value
*/
public boolean getBoolean(String attribute, String namespace, boolean defaultValue) {
String s = get(attribute, namespace);
if ("true".equals(s) || "1".equals(s)) {
return true;
} else
if ("false".equals(s) || "0".equals(s)) {
return false;
}
return defaultValue;
}
/**
* Returns the attribute as a double value.
* @param name the attribute name
* @return the value
*/
public double getDouble(String name) {
return Double.parseDouble(get(name));
}
/**
* Returns the attribute as a double value or the default.
* @param name the attribute name
* @param defaultValue the default if the attribute is missing
* @return the value
*/
public double getDouble(String name, double defaultValue) {
String v = get(name);
return v != null ? Double.parseDouble(v) : defaultValue;
}
/**
* Returns the attribute as a double value.
* @param name the attribute name
* @param namespace the attribute namespace
* @return the value
*/
public double getDouble(String name, String namespace) {
return Double.parseDouble(get(name, namespace));
}
/**
* Returns the attribute as a double value or the default.
* @param name the attribute name
* @param namespace the attribute namespace
* @param defaultValue the default if the attribute is missing
* @return the value
*/
public double getDouble(String name, String namespace, double defaultValue) {
String v = get(name, namespace);
return v != null ? Double.parseDouble(v) : defaultValue;
}
/**
* Retrieve an integer attribute. Throws exception if the attribute is missing.
* @param attribute the attribute name
* @return the value
*/
public int getInt(String attribute) {
return Integer.parseInt(get(attribute));
}
/**
* Retrieve an integer attribute or the default value if not exists.
* @param attribute the attribute name
* @param defaultValue the default value to return
* @return the value
*/
public int getInt(String attribute, int defaultValue) {
String value = get(attribute);
if (value == null) {
return defaultValue;
}
return Integer.parseInt(value);
}
/**
* Retrieve an integer attribute or the default value if not exists.
* @param attribute the attribute name
* @param namespace the attribute namespace URI
* @return the value
*/
public int getInt(String attribute, String namespace) {
return Integer.parseInt(get(attribute, namespace));
}
/**
* Retrieve an integer attribute or the default value if not exists.
* @param attribute the attribute name
* @param namespace the namespace URI
* @param defaultValue the default value to return
* @return the value
*/
public int getInt(String attribute, String namespace, int defaultValue) {
String value = get(attribute, namespace);
if (value == null) {
return defaultValue;
}
return Integer.parseInt(value);
}
/**
* Retrieve an long attribute or throw an exception if not exists.
* @param attribute the attribute name
* @return the value
*/
public long getLong(String attribute) {
return Long.parseLong(get(attribute));
}
/**
* Retrieve an long attribute or the default value if not exists.
* @param attribute the attribute name
* @param defaultValue the default value to return
* @return the value
*/
public long getLong(String attribute, long defaultValue) {
String value = get(attribute);
if (value == null) {
return defaultValue;
}
return Long.parseLong(value);
}
/**
* Retrieve an integer attribute or throw an exception if not exists.
* @param attribute the attribute name
* @param namespace the attribute namespace URI
* @return the value
*/
public long getLong(String attribute, String namespace) {
return Long.parseLong(get(attribute, namespace));
}
/**
* Retrieve an integer attribute or the default value if not exists.
* @param attribute the attribute name
* @param namespace the attribute namespace URI
* @param defaultValue the default value to return
* @return the value
*/
public long getLong(String attribute, String namespace, long defaultValue) {
String value = get(attribute, namespace);
if (value == null) {
return defaultValue;
}
return Long.parseLong(value);
}
/**
* @return Construct the XPath expression to locate this element.
*/
public String getXPath() {
StringBuilder r = new StringBuilder();
if (parent != null) {
XNElement q = this;
XNElement p = parent;
while (p != null) {
if (p.children.size() > 1) {
int count = 0;
int idx = 0;
for (XNElement c : p.children) {
if (q.name.equals(c.name) && Objects.equals(q.namespace, c.namespace)) {
if (c == q) {
idx = count;
break;
}
count++;
}
}
if (idx > 0) {
r.insert(0, ']');
r.insert(0, idx);
r.insert(0, '[');
r.insert(0, p.name);
r.insert(0, '/');
} else {
r.insert(0, p.name);
r.insert(0, '/');
}
} else {
r.insert(0, p.name);
r.insert(0, '/');
}
q = p;
p = p.parent;
}
}
r.append('/').append(name);
return r.toString();
}
/**
* Check if the simply-named attribute exists.
* @param name the name of the attribute
* @return true if the attribute exists
*/
public boolean hasAttribute(String name) {
return get(name) != null;
}
/**
* Check if the attribute exists.
* @param name the attribute name
* @param namespace the attribute namespace
* @return true if the attribute exists
*/
public boolean hasAttribute(String name, String namespace) {
return get(name, namespace) != null;
}
/** @return if this node has attributes or not. */
public boolean hasAttributes() {
return !attributes.isEmpty();
}
/** @return if this node has children or not. */
public boolean hasChildren() {
return !children.isEmpty();
}
@Override
public int hashCode() {
return Objects.hash(attributes, content, children);
}
/**
* Test if this XElement has the given name.
* @param name the name to test against
* @return true if the names equal
*/
public boolean hasName(String name) {
return Objects.equals(this.name, name);
}
/**
* Test if this XElement has the given name and namespace.
* @param name the name to test against
* @param namespace the namespace to test against
* @return true if the names and namespaces equal
*/
public boolean hasName(String name, String namespace) {
return hasName(name) && Objects.equals(this.namespace, namespace);
}
/**
* Returns an integer value of the supplied child or throws an exception if missing.
* @param name the child element name
* @param namespace the element namespace
* @return the value
*/
public int intValue(String name, String namespace) {
String s = childValue(name, namespace);
if (s != null) {
return Integer.parseInt(s);
}
throw new IllegalArgumentException(this + ": content: " + name);
}
/**
* Returns an integer value or the default value if the element is missing or empty.
* @param name the element name
* @param namespace the element namespace
* @param defaultValue the default value
* @return the value
*/
public int intValue(String name, String namespace, int defaultValue) {
String s = childValue(name, namespace);
if (s != null) {
return Integer.parseInt(s);
}
return defaultValue;
}
/**
* Returns a long value of the supplied child or throws an exception if missing.
* @param name the child element name
* @param namespace the element namespace
* @return the value
*/
public long longValue(String name, String namespace) {
String s = childValue(name, namespace);
if (s != null) {
return Long.parseLong(s);
}
throw new IllegalArgumentException(this + ": content: " + name);
}
/**
* Returns a long value or the default value if the element is missing or empty.
* @param name the element name
* @param namespace the element namespace
* @param defaultValue the default value
* @return the value
*/
public long longValue(String name, String namespace, long defaultValue) {
String s = childValue(name, namespace);
if (s != null) {
return Long.parseLong(s);
}
return defaultValue;
}
/**
* Save this XML into the given file.
* @param file the file
* @throws IOException on error
*/
public void save(File file) throws IOException {
try (FileOutputStream fout = new FileOutputStream(file)) {
save(fout);
}
}
/**
* Save this XML into the given output stream.
* Does not close the given stream
* @param stream the output stream
* @throws IOException on error
*/
public void save(OutputStream stream) throws IOException {
save(new OutputStreamWriter(stream, "UTF-8"));
}
/**
* Save this XML into the given file.
* @param fileName the file name
* @throws IOException on error
*/
public void save(String fileName) throws IOException {
save(new File(fileName));
}
/**
* Save this XML into the given writer.
* Does not close the writer.
* @param stream the output writer
* @throws IOException on error
*/
public void save(Writer stream) throws IOException {
final PrintWriter out = new PrintWriter(new BufferedWriter(stream));
try {
out.println("");
toStringRep("", new HashMap(), new XNAppender() {
@Override
public XNAppender append(Object o) {
out.print(o);
return this;
}
@Override
public int length() {
return 0; // Not used here
}
}, null);
} finally {
out.flush();
}
}
/**
* Save the tree into the given XML stream writer.
* @param stream the stream writer
* @throws XMLStreamException if an error occurs
*/
public void save(XMLStreamWriter stream) throws XMLStreamException {
stream.writeStartDocument("UTF-8", "1.0");
saveInternal(stream);
stream.writeEndDocument();
}
/**
* Store the element's content and recursively call itself for children.
* @param stream the stream output
* @throws XMLStreamException if an error occurs
*/
protected void saveInternal(XMLStreamWriter stream) throws XMLStreamException {
if (namespace != null) {
stream.writeStartElement(prefix, namespace, name);
} else {
stream.writeStartElement(name);
}
for (Map.Entry a : attributes.entrySet()) {
XAttributeName an = a.getKey();
if (an.namespace != null) {
stream.writeAttribute(an.prefix, an.namespace, an.name, a.getValue());
} else {
stream.writeAttribute(an.name, a.getValue());
}
}
if (content != null) {
stream.writeCharacters(content);
} else {
for (XNElement e : children) {
e.saveInternal(stream);
}
}
stream.writeEndElement();
}
/**
* Set a multitude of attribute names and values.
* @param nameValues the attribute name and attribute value pairs.
*/
public void set(Object... nameValues) {
if (nameValues.length % 2 != 0) {
throw new IllegalArgumentException("Names and values must be in pairs");
}
for (int i = 0; i < nameValues.length; i += 2) {
set((String)nameValues[i], nameValues[i + 1]);
}
}
/**
* Set an attribute value identified by a local name and no namespace.
* @param name the attribute name
* @param value the value, if null, the attribute will be removed
*/
public void set(String name, Object value) {
set(name, null, value);
}
/**
* Set an attribute value identified by a local name and namespace.
* @param name the attribute name
* @param namespace the attribute namespace
* @param value the value, if null, the attribute will be removed
*/
public void set(String name, String namespace, Object value) {
if (value != null) {
if (value instanceof Date) {
attributes.put(new XAttributeName(name, namespace, null), formatDateTime((Date)value));
} else {
attributes.put(new XAttributeName(name, namespace, null), value.toString());
}
} else {
attributes.remove(new XAttributeName(name, namespace, null));
}
}
/**
* Replace the associated user object with a new object.
* @param the object type
* @param newUserObject the new user object
* @return the original user object
*/
@SuppressWarnings("unchecked")
public T set(T newUserObject) {
T result = (T)userObject;
userObject = newUserObject;
return result;
}
@Override
public String toString() {
final StringBuilder b = new StringBuilder();
toStringRep("", new HashMap(), new XNAppender() {
@Override
public XNAppender append(Object o) {
b.append(o);
return this;
}
@Override
public int length() {
return b.length();
}
}, null);
return b.toString();
}
/**
* Convert the element into a pretty printed string representation.
* @param indent the current line indentation
* @param nss the namespace cache
* @param out the output
* @param callback the callback for each element and text position.
*/
public void toStringRep(String indent, Map nss,
XNAppender out, Consumer super XRepresentationRecord> callback) {
Map nss0 = new HashMap<>(nss);
out.append(indent);
if (callback != null) {
callback.accept(new XRepresentationRecord(XRepresentationState.START_ELEMENT, this,
out.length()));
}
out.append("<");
Pair pf = createPrefix(nss0, namespace, prefix);
String prefix = pf.first;
if (prefix != null && prefix.length() > 0) {
out.append(prefix).append(":");
}
out.append(name);
if (pf.second != null) {
out.append(pf.second);
}
if (attributes.size() > 0) {
for (XAttributeName an : attributes.keySet()) {
Pair pfa = createPrefix(nss0, an.namespace, an.prefix);
out.append(" ");
if (pfa.first != null && pfa.first.length() > 0) {
out.append(pfa.first).append(":");
}
out.append(an.name).append("='").append(sanitize(attributes.get(an))).append("'");
if (pfa.second != null) {
out.append(pfa.second);
}
}
}
if (children.size() == 0) {
if (content == null || content.isEmpty()) {
out.append("/>");
} else {
out.append(">");
if (callback != null) {
callback.accept(new XRepresentationRecord(XRepresentationState.START_TEXT, this, out.length()));
}
String s = sanitize(content);
out.append(s);
if (callback != null) {
callback.accept(new XRepresentationRecord(XRepresentationState.END_TEXT, this, out.length()));
}
if (s.endsWith("\n")) {
out.append(indent);
}
out.append("");
if (prefix != null && prefix.length() > 0) {
out.append(prefix).append(":");
}
out.append(name);
out.append(">");
}
} else {
if (content == null || content.isEmpty()) {
out.append(String.format(">%n"));
} else {
out.append(">");
if (callback != null) {
callback.accept(new XRepresentationRecord(XRepresentationState.START_TEXT, this, out.length()));
}
out.append(sanitize(content));
if (callback != null) {
callback.accept(new XRepresentationRecord(XRepresentationState.END_TEXT, this, out.length()));
}
out.append(String.format("%n"));
}
for (XNElement e : children) {
e.toStringRep(indent + " ", nss0, out, callback);
}
out.append(indent).append("");
if (prefix != null && prefix.length() > 0) {
out.append(prefix).append(":");
}
out.append(name);
out.append(">");
}
if (callback != null) {
callback.accept(new XRepresentationRecord(XRepresentationState.END_ELEMENT, this, out.length()));
}
out.append(String.format("%n"));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy