Dépôt officiel du code source de l'ERP OpenConcerto
Blame | Last modification | View Log | RSS feed
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.utils.doml;
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
public class Element {
private String name;
private List<String> attributes;
private List<Element> children;
private static final ThreadLocal<SimpleDateFormat> DATE_FORMAT = ThreadLocal.withInitial(() -> {
final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
return dateFormat;
});
private static final ThreadLocal<DecimalFormat> DOUBLE_FORMAT = ThreadLocal.withInitial(() -> {
final DecimalFormat f = new DecimalFormat("0", DecimalFormatSymbols.getInstance(Locale.ENGLISH));
f.setMaximumFractionDigits(340); // 340 = DecimalFormat.DOUBLE_FRACTION_DIGITS
return f;
});
private static final ThreadLocal<DecimalFormat> FLOAT_FORMAT = ThreadLocal.withInitial(() -> {
final DecimalFormat f = new DecimalFormat("0", DecimalFormatSymbols.getInstance(Locale.ENGLISH));
f.setMaximumFractionDigits(170);
return f;
});
/**
* Element names are case-sensitive
*
* Element names can contain letters, digits,colons, hyphens, underscores, and periods.
*
* Element names cannot contain spaces
*/
public Element(String name) {
if (name == null) {
throw new IllegalArgumentException("null name");
}
this.name = name;
}
public Element(String name, int expectedAttributesCount) {
if (name == null) {
throw new IllegalArgumentException("null name");
}
this.name = name;
if (expectedAttributesCount > 0) {
this.attributes = new ArrayList<>(expectedAttributesCount * 2);
}
}
public Element(String name, int expectedAttributesCount, int expecteChildrenCount) {
if (name == null) {
throw new IllegalArgumentException("null name");
}
this.name = name;
if (expectedAttributesCount > 0) {
this.attributes = new ArrayList<>(expectedAttributesCount * 2);
}
if (expecteChildrenCount > 0) {
this.children = new ArrayList<>(expecteChildrenCount);
}
}
public String getName() {
return this.name;
}
public void setName(String name) {
if (name == null) {
throw new IllegalArgumentException("null name");
}
this.name = name;
}
public int getAttributeCount() {
if (this.attributes == null)
return 0;
return this.attributes.size() / 2;
}
// String
public String getAttribute(String name) {
if (name == null) {
throw new IllegalArgumentException("null name");
}
if (this.attributes == null) {
return null;
}
final int size = this.attributes.size();
for (int i = 0; i < size; i += 2) {
if (this.attributes.get(i).equals(name)) {
return this.attributes.get(i + 1);
}
}
return null;
}
public String getAttribute(String name, String defaultValue) {
final String v = getAttribute(name);
if (v == null) {
return defaultValue;
}
return v;
}
public void setAttribute(String name, String value) {
if (name == null) {
throw new IllegalArgumentException("null name");
}
if (value == null) {
throw new IllegalArgumentException("null value for attribute " + name);
}
if (this.attributes == null) {
this.attributes = new ArrayList<>();
} else {
final int size = this.attributes.size();
for (int i = 0; i < size; i += 2) {
if (this.attributes.get(i).equals(name)) {
this.attributes.set(i + 1, value);
return;
}
}
}
this.attributes.add(name);
this.attributes.add(value);
}
// Integer
public Integer getAttributeAsInteger(String name) {
final String v = getAttribute(name);
if (v == null) {
return null;
}
return Integer.parseInt(v);
}
public int getAttributeAsInteger(String name, int defaultValue) {
final String v = getAttribute(name);
if (v == null) {
return defaultValue;
}
return Integer.parseInt(v);
}
public void setAttribute(String name, int value) {
setAttribute(name, String.valueOf(value));
}
// Long
public Long getAttributeAsLong(String name) {
final String v = getAttribute(name);
if (v == null) {
return null;
}
return Long.parseLong(v);
}
public long getAttributeAsLong(String name, long defaultValue) {
final String v = getAttribute(name);
if (v == null) {
return defaultValue;
}
return Long.parseLong(v);
}
public void setAttribute(String name, long value) {
setAttribute(name, String.valueOf(value));
}
// Float
public Float getAttributeAsFloat(String name) {
final String v = getAttribute(name);
if (v == null) {
return null;
}
return parseFloat(v);
}
public float getAttributeAsFloat(String name, float defaultValue) {
final String v = getAttribute(name);
if (v == null) {
return defaultValue;
}
return parseFloat(v);
}
public void setAttribute(String name, float value) {
setAttribute(name, floatToString(value));
}
// Double
public Double getAttributeAsDouble(String name) {
final String v = getAttribute(name);
if (v == null) {
return null;
}
return parseDouble(name);
}
public double getAttributeAsLong(String name, double defaultValue) {
final String v = getAttribute(name);
if (v == null) {
return defaultValue;
}
return parseDouble(name);
}
public void setAttribute(String name, double value) {
setAttribute(name, doubleToString(value));
}
public static String doubleToString(final double d) {
if (StrictMath.rint(d) == d && Double.isFinite(d)) {
return Long.toString((long) d);
} else {
return DOUBLE_FORMAT.get().format(d);
}
}
private static double parseDouble(String v) {
boolean neg = false;
int start = 0;
if (v.charAt(0) == '-') {
neg = true;
start = 1;
}
double result = 0;
int length = v.length();
int pointIndex = length;
for (int i = start; i < length; i++) {
char c = v.charAt(i);
if (c == '.') {
pointIndex = i;
break;
}
}
for (int i = start; i < pointIndex; i++) {
result = 10 * result + (v.charAt(i) - '0');
}
int n = 0;
int div = 1;
for (int i = pointIndex + 1; i < length; i++) {
n = 10 * n + (v.charAt(i) - '0');
div = div * 10;
}
result += ((double) n) / div;
if (neg) {
result = (result * -1f);
}
return result;
}
public static String floatToString(final double d) {
if (StrictMath.rint(d) == d && Double.isFinite(d)) {
return Long.toString((long) d);
} else {
return FLOAT_FORMAT.get().format(d);
}
}
private static float parseFloat(String v) {
boolean neg = false;
int start = 0;
if (v.charAt(0) == '-') {
neg = true;
start = 1;
}
float result = 0;
int length = v.length();
int pointIndex = length;
for (int i = start; i < length; i++) {
char c = v.charAt(i);
if (c == '.') {
pointIndex = i;
break;
}
}
for (int i = start; i < pointIndex; i++) {
result = 10 * result + (v.charAt(i) - '0');
}
int n = 0;
int div = 1;
for (int i = pointIndex + 1; i < length; i++) {
n = 10 * n + (v.charAt(i) - '0');
div = div * 10;
}
result += ((float) n) / div;
if (neg) {
result = (result * -1f);
}
return result;
}
// BigDecimal
public BigDecimal getAttributeAsBigDecimal(String name) {
final String v = getAttribute(name);
if (v == null) {
return null;
}
return new BigDecimal(v);
}
public BigDecimal getAttributeAsBigDecimal(String name, BigDecimal defaultValue) {
final String v = getAttribute(name);
if (v == null) {
return defaultValue;
}
return new BigDecimal(v);
}
public void setAttribute(String name, BigDecimal value) {
setAttribute(name, String.valueOf(value));
}
// Boolean
public Boolean getAttributeAsBoolean(String name) {
final String v = getAttribute(name);
if (v == null) {
return null;
}
if (name.equals("true")) {
return Boolean.TRUE;
}
return Boolean.FALSE;
}
public boolean getAttributeAsBoolean(String name, boolean defaultValue) {
final String v = getAttribute(name);
if (v == null) {
return defaultValue;
}
return name.equals("true");
}
public void setAttribute(String name, boolean value) {
setAttribute(name, value ? "true" : "false");
}
// Date
public Date getAttributeAsDate(String name) {
final String v = getAttribute(name);
if (v == null)
return null;
try {
return DATE_FORMAT.get().parse(v);
} catch (ParseException e) {
throw new IllegalStateException("cannot parse date : '" + v + "' : " + e);
}
}
public Date getAttributeAsDate(String name, Date defaultValue) {
final Date v = getAttributeAsDate(name);
if (v == null)
return defaultValue;
return v;
}
public void setAttribute(String name, Date value) {
setAttribute(name, value.toInstant().toString());
}
// Instant
public Instant getAttributeAsInstant(String name) {
String v = getAttribute(name);
if (v == null)
return null;
return Instant.parse(v);
}
public void setAttribute(String name, Instant value) {
setAttribute(name, value.toString());
}
public void addChild(Element child) {
if (child == null) {
throw new IllegalArgumentException("null child");
}
if (this.children == null) {
this.children = new ArrayList<>();
}
this.children.add(child);
}
public void addChildren(List<Element> children) {
if (children == null) {
throw new IllegalArgumentException("null children");
}
if (this.children == null) {
this.children = new ArrayList<>(children.size() * 2);
}
this.children.addAll(children);
}
/**
* This returns the first child element within this element with the given name. If no elements
* exist for the specified name and namespace, null is returned.
*
* @param name of child element to match
* @return the first matching child element, or null if not found
*/
public Element getChild(String name) {
if (name == null) {
throw new IllegalArgumentException("null name");
}
if (this.children == null) {
return null;
}
for (Element e : this.children) {
if (e.getName().equals(name)) {
return e;
}
}
return null;
}
public List<Element> getChildren(String name) {
if (name == null) {
throw new IllegalArgumentException("null name");
}
if (this.children == null) {
return Collections.emptyList();
}
final List<Element> result = new ArrayList<>();
for (Element e : this.children) {
if (e.getName().equals(name)) {
result.add(e);
}
}
return result;
}
/**
* This returns a <code>List</code> of all the child elements nested directly (one level deep)
* within this element, as <code>Element</code> objects. If this target element has no nested
* elements, an empty List is returned. The returned list is unmodifiable.
*
* No recursion is performed.
*
* @return list of child <code>XXMLElement</code> objects for this element
*/
public List<Element> getChildren() {
if (this.children == null) {
return Collections.emptyList();
}
return Collections.unmodifiableList(children);
}
/**
* <p>
* This removes the first child element (one level deep) with the given name. Returns true if a
* child was removed.
* </p>
*
* @param name of child element to remove
* @return whether deletion occurred
*/
public boolean removeChild(final String name) {
if (name == null) {
throw new IllegalArgumentException("null name");
}
if (this.children == null) {
return false;
}
final int size = this.children.size();
for (int i = 0; i < size; i++) {
if (this.children.get(i).getName().equals(name)) {
this.children.remove(i);
return true;
}
}
return false;
}
public int getChildrenCount() {
if (this.children == null) {
return 0;
}
return this.children.size();
}
void appendAttributes(BufferedWriter writer) throws IOException {
if (this.attributes == null) {
return;
}
final int size = this.attributes.size();
for (int i = 0; i < size; i += 2) {
writer.append(' ');
writer.append(this.attributes.get(i));
writer.append('=');
writer.append('\"');
writer.append(escapeAttributeValue(this.attributes.get(i + 1)));
writer.append('\"');
}
}
void writeAttributes(BufferedOutputStream out) throws IOException {
final int size = this.attributes.size();
for (int i = 0; i < size; i += 2) {
Document.writeUTF(out, this.attributes.get(i));
Document.writeUTF(out, this.attributes.get(i + 1));
}
}
private String escapeAttributeValue(String string) {
StringBuilder b = new StringBuilder(string.length());
int length = string.length();
for (int i = 0; i < length; i++) {
char c = string.charAt(i);
if (c == '\n') {
b.append('\\');
b.append('n');
} else if (c == '\r') {
b.append('\\');
b.append('r');
} else if (c == '\t') {
b.append('\\');
b.append('t');
} else if (c == '\\') {
b.append('\\');
b.append('\\');
} else if (c == '"') {
b.append('\\');
b.append('"');
} else {
b.append(c);
}
}
return b.toString();
}
void addAttributeNoCheck(String name, String value) {
this.attributes.add(name);
this.attributes.add(value);
}
void addChildNoCheck(Element child) {
if (child == null) {
throw new IllegalArgumentException("null child");
}
this.children.add(child);
}
}