OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

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);
    }

}