/*
* $Id$
* This file is a part of the Arakhne Foundation Classes, http://www.arakhne.org/afc
*
* Copyright (c) 2000-2012 Stephane GALLAND.
* Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
* Universite de Technologie de Belfort-Montbeliard.
* Copyright (c) 2013-2016 The original authors, and other authors.
*
* 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 org.arakhne.afc.inputoutput.xml;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.Serializable;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.eclipse.xtext.xbase.lib.Pure;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.arakhne.afc.text.Base64Coder;
import org.arakhne.afc.vmutil.ClassLoaderFinder;
import org.arakhne.afc.vmutil.FileSystem;
import org.arakhne.afc.vmutil.asserts.AssertMessages;
import org.arakhne.afc.vmutil.locale.Locale;
/**
* Utility class for manipulating XML data and files.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
* @since 14.0
*/
@SuppressWarnings({"checkstyle:methodcount", "checkstyle:classfanoutcomplexity", "checkstyle:classdataabstractioncoupling"})
public final class XMLUtil {
/** Format of the date in XML files.
* The dateTime is specified in the following form "YYYY-MM-DDThh:mm:ss" where:<ul>
* <li>YYYY indicates the year</li>
* <li>MM indicates the month</li>
* <li>DD indicates the day</li>
* <li>T indicates the start of the required time section</li>
* <li>hh indicates the hour</li>
* <li>mm indicates the minute</li>
* <li>ss indicates the second</li>
* </ul>
*/
public static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss"; //$NON-NLS-1$
/** <code>id=""</code>.
*/
public static final String ATTR_ID = "id"; //$NON-NLS-1$
/** <code>name=""</code>.
*/
public static final String ATTR_NAME = "name"; //$NON-NLS-1$
/** List of XML colors and there corresponding Java colors.
*/
public static final Map<String, Integer> COLOR_MATCHES;
private static final String COLOR_DEFINITION_PATTERN = "^\\s*([^:\\s*]+)\\s*:\\s*([0-9]+)" //$NON-NLS-1$
+ "\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*$"; //$NON-NLS-1$
private static final String EOL_PATTERN = "[\n\r]+"; //$NON-NLS-1$
private static final String CONSTANT_TRUE = "true"; //$NON-NLS-1$
private static final String CONSTANT_YES = "yes"; //$NON-NLS-1$
private static final String CONSTANT_ON = "on"; //$NON-NLS-1$
private static final String CONSTANT_Y = "y"; //$NON-NLS-1$
private static final String CONSTANT_T = "t"; //$NON-NLS-1$
private static final String COLUMN_SEPARATOR = "[ \t]*;[ \t]*"; //$NON-NLS-1$
private static final String INDENT_NUMBER = "indent-number"; //$NON-NLS-1$
static {
COLOR_MATCHES = initializeColors();
}
private XMLUtil() {
//
}
@SuppressWarnings("checkstyle:magicnumber")
private static Map<String, Integer> initializeColors() {
final Map<String, Integer> map = new HashMap<>();
final String colors = Locale.getString(XMLUtil.class, "COLOR_MATCHES"); //$NON-NLS-1$
if (colors != null) {
final Pattern pattern = Pattern.compile(COLOR_DEFINITION_PATTERN);
for (final String definition : colors.split(EOL_PATTERN)) {
final Matcher matcher = pattern.matcher(definition);
if (matcher.matches()) {
map.put(matcher.group(1),
encodeColor(
Integer.parseInt(matcher.group(2)),
Integer.parseInt(matcher.group(3)),
Integer.parseInt(matcher.group(4)),
Integer.parseInt(matcher.group(5))));
}
}
}
return map;
}
@SuppressWarnings("checkstyle:magicnumber")
private static int decodeColor(String colorString) {
final Integer intval = Integer.decode(colorString);
return intval.intValue();
}
@SuppressWarnings("checkstyle:magicnumber")
private static Integer encodeColor(int red, int green, int blue, int alpha) {
int col = (alpha & 0xFF) << 24;
col |= (red & 0xFF) << 16;
col |= (green & 0xFF) << 8;
col |= blue & 0xFF;
return Integer.valueOf(col);
}
/** Replies the boolean value that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
*
* @param document is the XML document to explore.
* @param caseSensitive indicates of the {@code path}'s components are case sensitive.
* @param path is the list of and ended by the attribute's name.
* @return the boolean value of the specified attribute or <code>false</code> if
* it was node found in the document
*/
@Pure
public static boolean getAttributeBoolean(Node document, boolean caseSensitive, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getAttributeBooleanWithDefault(document, true, false, path);
}
/** Replies the boolean value that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
* Be careful about the fact that the names are case sensitives.
*
* @param document is the XML document to explore.
* @param path is the list of and ended by the attribute's name.
* @return the boolean value of the specified attribute or <code>0</code>.
*/
@Pure
public static boolean getAttributeBoolean(Node document, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getAttributeBooleanWithDefault(document, true, false, path);
}
/** Replies the boolean value that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
*
* @param document is the XML document to explore.
* @param caseSensitive indicates of the {@code path}'s components are case sensitive.
* @param defaultValue is the default value to reply.
* @param path is the list of and ended by the attribute's name.
* @return the boolean value of the specified attribute or <code>false</code> if
* it was node found in the document
*/
@Pure
public static boolean getAttributeBooleanWithDefault(Node document, boolean caseSensitive,
boolean defaultValue, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
final String v = getAttributeValue(document, caseSensitive, 0, path);
if (v == null || v.isEmpty()) {
return defaultValue;
}
return CONSTANT_TRUE.equalsIgnoreCase(v)
|| CONSTANT_YES.equalsIgnoreCase(v)
|| CONSTANT_ON.equalsIgnoreCase(v)
|| CONSTANT_Y.equalsIgnoreCase(v)
|| CONSTANT_T.equalsIgnoreCase(v);
}
/** Replies the boolean value that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
*
* @param document is the XML document to explore.
* @param defaultValue is the default value to reply.
* @param path is the list of and ended by the attribute's name.
* @return the boolean value of the specified attribute or <code>false</code> if
* it was node found in the document
*/
@Pure
public static boolean getAttributeBooleanWithDefault(Node document, boolean defaultValue, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getAttributeBooleanWithDefault(document, true, defaultValue, path);
}
/** Read an enumeration value.
*
* @param document is the XML document to explore.
* @param caseSensitive indicates of the {@code path}'s components are case sensitive.
* @param path is the list of and ended by the attribute's name.
* @return the java class or <code>null</code> if none.
*/
@Pure
public static Class<?> getAttributeClass(Node document, boolean caseSensitive, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getAttributeClassWithDefault(document, caseSensitive, null, path);
}
/** Read an enumeration value.
*
* @param document is the XML document to explore.
* @param path is the list of and ended by the attribute's name.
* @return the java class or <code>null</code> if none.
*/
@Pure
public static Class<?> getAttributeClass(Node document, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getAttributeClassWithDefault(document, true, null, path);
}
/** Read a java class.
*
* @param document is the XML document to explore.
* @param caseSensitive indicates of the {@code path}'s components are case sensitive.
* @param defaultValue is the default value replied if no attribute was found.
* @param path is the list of and ended by the attribute's name.
* @return the java class or <code>defaultValue</code> if none.
*/
@Pure
public static Class<?> getAttributeClassWithDefault(Node document, boolean caseSensitive,
Class<?> defaultValue, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
final String v = getAttributeValue(document, caseSensitive, 0, path);
if (v != null && !v.isEmpty()) {
try {
final ClassLoader loader = ClassLoaderFinder.findClassLoader();
return loader.loadClass(v);
} catch (Throwable e) {
//
}
}
return defaultValue;
}
/** Read an enumeration value.
*
* @param document is the XML document to explore.
* @param defaultValue is the default value replied if no attribute was found.
* @param path is the list of and ended by the attribute's name.
* @return the java class or <code>defaultValue</code> if none.
*/
@Pure
public static Class<?> getAttributeClassWithDefault(Node document, Class<?> defaultValue, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getAttributeClassWithDefault(document, true, defaultValue, path);
}
/** Replies the color that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
*
* @param document is the XML document to explore.
* @param caseSensitive indicates of the {@code path}'s components are case sensitive.
* @param path is the list of and ended by the attribute's name.
* @return the color of the specified attribute.
*/
@Pure
public static int getAttributeColor(Node document, boolean caseSensitive, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getAttributeColorWithDefault(document, caseSensitive, 0, path);
}
/** Replies the color that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
* Be careful about the fact that the names are case sensitives.
*
* @param document is the XML document to explore.
* @param path is the list of and ended by the attribute's name.
* @return the color of the specified attribute.
*/
@Pure
public static int getAttributeColor(Node document, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getAttributeColorWithDefault(document, true, 0, path);
}
/** Replies the color that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
*
* @param document is the XML document to explore.
* @param caseSensitive indicates of the {@code path}'s components are case sensitive.
* @param defaultValue is the default value to reply.
* @param path is the list of and ended by the attribute's name.
* @return the color of the specified attribute or <code>null</code> if
* it was node found in the document
*/
@Pure
public static int getAttributeColorWithDefault(Node document, boolean caseSensitive, int defaultValue,
String... path) {
assert document != null : AssertMessages.notNullParameter(0);
final String v = getAttributeValue(document, caseSensitive, 0, path);
if (v != null) {
try {
return parseColor(v);
} catch (ColorFormatException e) {
//
}
}
return defaultValue;
}
/** Replies the color that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
* Be careful about the fact that the names are case sensitives.
*
* @param document is the XML document to explore.
* @param path is the list of and ended by the attribute's name.
* @param defaultValue is the default value to reply.
* @return the color of the specified attribute.
*/
@Pure
public static int getAttributeColorWithDefault(Node document, int defaultValue, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getAttributeColorWithDefault(document, true, defaultValue, path);
}
/** Replies the date that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
*
* @param document is the XML document to explore.
* @param caseSensitive indicates of the {@code path}'s components are case sensitive.
* @param path is the list of and ended by the attribute's name.
* @return the date of the specified attribute or <code>null</code> if
* it was node found in the document
*/
@Pure
public static Date getAttributeDate(Node document, boolean caseSensitive, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getAttributeDateWithDefault(document, caseSensitive, null, path);
}
/** Replies the date that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
* Be careful about the fact that the names are case sensitives.
*
* @param document is the XML document to explore.
* @param path is the list of and ended by the attribute's name.
* @return the date of the specified attribute or <code>null</code> if
* it was node found in the document
*/
@Pure
public static Date getAttributeDate(Node document, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getAttributeDateWithDefault(document, true, null, path);
}
/** Replies the date that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
*
* @param document is the XML document to explore.
* @param caseSensitive indicates of the {@code path}'s components are case sensitive.
* @param defaultValue is the default value to reply.
* @param path is the list of and ended by the attribute's name.
* @return the date of the specified attribute or <code>null</code> if
* it was node found in the document
*/
@Pure
public static Date getAttributeDateWithDefault(Node document, boolean caseSensitive, Date defaultValue, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
final String v = getAttributeValue(document, caseSensitive, 0, path);
if (v != null) {
try {
return parseDate(v);
} catch (DateFormatException e) {
//
}
}
return defaultValue;
}
/** Replies the date that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
* Be careful about the fact that the names are case sensitives.
*
* @param document is the XML document to explore.
* @param path is the list of and ended by the attribute's name.
* @param defaultValue is the default value to reply.
* @return the date of the specified attribute or <code>null</code> if
* it was node found in the document
*/
@Pure
public static Date getAttributeDateWithDefault(Node document, Date defaultValue, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getAttributeDateWithDefault(document, true, defaultValue, path);
}
/** Replies the double value that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
*
* @param document is the XML document to explore.
* @param caseSensitive indicates of the {@code path}'s components are case sensitive.
* @param path is the list of and ended by the attribute's name.
* @return the double value of the specified attribute or <code>null</code> if
* it was node found in the document
*/
@Pure
public static double getAttributeDouble(Node document, boolean caseSensitive, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getAttributeDoubleWithDefault(document, caseSensitive, 0., path);
}
/** Replies the double value that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
* Be careful about the fact that the names are case sensitives.
*
* @param document is the XML document to explore.
* @param path is the list of and ended by the attribute's name.
* @return the double value of the specified attribute or <code>0</code>.
*/
@Pure
public static double getAttributeDouble(Node document, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getAttributeDoubleWithDefault(document, true, 0., path);
}
/** Replies the double value that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
*
* @param document is the XML document to explore.
* @param caseSensitive indicates of the {@code path}'s components are case sensitive.
* @param defaultValue is the default value to reply.
* @param path is the list of and ended by the attribute's name.
* @return the double value of the specified attribute or <code>null</code> if
* it was node found in the document
*/
@Pure
public static double getAttributeDoubleWithDefault(Node document, boolean caseSensitive, double defaultValue,
String... path) {
assert document != null : AssertMessages.notNullParameter(0);
final String v = getAttributeValue(document, caseSensitive, 0, path);
if (v != null) {
try {
return Double.parseDouble(v);
} catch (NumberFormatException e) {
//
}
}
return defaultValue;
}
/** Replies the double value that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
* Be careful about the fact that the names are case sensitives.
*
* @param document is the XML document to explore.
* @param defaultValue is the default value to reply.
* @param path is the list of and ended by the attribute's name.
* @return the double value of the specified attribute or <code>0</code>.
*/
@Pure
public static double getAttributeDoubleWithDefault(Node document, double defaultValue, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getAttributeDoubleWithDefault(document, true, defaultValue, path);
}
/** Read an enumeration value.
*
* @param <T> is the type of the enumeration.
* @param document is the XML document to explore.
* @param type is the type of the enumeration.
* @param caseSensitive indicates of the {@code path}'s components are case sensitive.
* @param path is the list of and ended by the attribute's name.
* @return the value of the enumeration or <code>null</code> if none.
*/
@Pure
public static <T extends Enum<T>> T getAttributeEnum(Node document, Class<T> type, boolean caseSensitive, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getAttributeEnumWithDefault(document, type, caseSensitive, null, path);
}
/** Read an enumeration value.
*
* @param <T> is the type of the enumeration.
* @param document is the XML document to explore.
* @param type is the type of the enumeration.
* @param path is the list of and ended by the attribute's name.
* @return the value of the enumeration or <code>null</code> if none.
*/
@Pure
public static <T extends Enum<T>> T getAttributeEnum(Node document, Class<T> type, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getAttributeEnumWithDefault(document, type, true, null, path);
}
/** Read an enumeration value.
*
* @param <T> is the type of the enumeration.
* @param document is the XML document to explore.
* @param type is the type of the enumeration.
* @param caseSensitive indicates of the {@code path}'s components are case sensitive.
* @param defaultValue is the default value replied if no attribute was found.
* @param path is the list of and ended by the attribute's name.
* @return the value of the enumeration or <code>null</code> if none.
*/
@Pure
public static <T extends Enum<T>> T getAttributeEnumWithDefault(Node document, Class<T> type,
boolean caseSensitive, T defaultValue, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
assert type != null : AssertMessages.notNullParameter(1);
final String v = getAttributeValue(document, caseSensitive, 0, path);
if (v != null && !v.isEmpty()) {
try {
final T value = Enum.valueOf(type, v);
if (value != null) {
return value;
}
} catch (Throwable e) {
//
}
}
return defaultValue;
}
/** Read an enumeration value.
*
* @param <T> is the type of the enumeration.
* @param document is the XML document to explore.
* @param type is the type of the enumeration.
* @param defaultValue is the default value replied if no attribute was found.
* @param path is the list of and ended by the attribute's name.
* @return the value of the enumeration or <code>null</code> if none.
*/
@Pure
public static <T extends Enum<T>> T getAttributeEnumWithDefault(Node document, Class<T> type,
T defaultValue, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getAttributeEnumWithDefault(document, type, true, defaultValue, path);
}
/** Replies the float value that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
*
* @param document is the XML document to explore.
* @param caseSensitive indicates of the {@code path}'s components are case sensitive.
* @param path is the list of and ended by the attribute's name.
* @return the float value of the specified attribute or <code>null</code> if
* it was node found in the document
*/
@Pure
public static float getAttributeFloat(Node document, boolean caseSensitive, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getAttributeFloatWithDefault(document, caseSensitive, 0f, path);
}
/** Replies the float value that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
* Be careful about the fact that the names are case sensitives.
*
* @param document is the XML document to explore.
* @param path is the list of and ended by the attribute's name.
* @return the float value of the specified attribute or <code>0</code>.
*/
@Pure
public static float getAttributeFloat(Node document, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getAttributeFloatWithDefault(document, true, 0f, path);
}
/** Replies the float value that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
*
* @param document is the XML document to explore.
* @param caseSensitive indicates of the {@code path}'s components are case sensitive.
* @param defaultValue is the default value to reply.
* @param path is the list of and ended by the attribute's name.
* @return the float value of the specified attribute or <code>null</code> if
* it was node found in the document
*/
@Pure
public static float getAttributeFloatWithDefault(Node document, boolean caseSensitive, float defaultValue, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
final String v = getAttributeValue(document, caseSensitive, 0, path);
if (v != null) {
try {
return Float.parseFloat(v);
} catch (NumberFormatException e) {
//
}
}
return defaultValue;
}
/** Replies the float value that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
* Be careful about the fact that the names are case sensitives.
*
* @param document is the XML document to explore.
* @param defaultValue is the default value to reply.
* @param path is the list of and ended by the attribute's name.
* @return the float value of the specified attribute or <code>0</code>.
*/
@Pure
public static float getAttributeFloatWithDefault(Node document, float defaultValue, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getAttributeFloatWithDefault(document, true, defaultValue, path);
}
/** Replies the integer value that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
*
* @param document is the XML document to explore.
* @param caseSensitive indicates of the {@code path}'s components are case sensitive.
* @param path is the list of and ended by the attribute's name.
* @return the integer value of the specified attribute or <code>null</code> if
* it was node found in the document
*/
@Pure
public static int getAttributeInt(Node document, boolean caseSensitive, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getAttributeIntWithDefault(document, caseSensitive, 0, path);
}
/** Replies the integer value that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
* Be careful about the fact that the names are case sensitives.
*
* @param document is the XML document to explore.
* @param path is the list of and ended by the attribute's name.
* @return the integer value of the specified attribute or <code>0</code>.
*/
@Pure
public static int getAttributeInt(Node document, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getAttributeIntWithDefault(document, true, 0, path);
}
/** Replies the integer value that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
*
* @param document is the XML document to explore.
* @param caseSensitive indicates of the {@code path}'s components are case sensitive.
* @param defaultValue is the default value to reply.
* @param path is the list of and ended by the attribute's name.
* @return the integer value of the specified attribute or <code>null</code> if
* it was node found in the document
*/
@Pure
public static int getAttributeIntWithDefault(Node document, boolean caseSensitive, int defaultValue, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
final String v = getAttributeValue(document, caseSensitive, 0, path);
if (v != null) {
try {
return Integer.parseInt(v);
} catch (NumberFormatException e) {
//
}
}
return defaultValue;
}
/** Replies the integer value that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
* Be careful about the fact that the names are case sensitives.
*
* @param document is the XML document to explore.
* @param defaultValue is the default value to reply.
* @param path is the list of and ended by the attribute's name.
* @return the integer value of the specified attribute or <code>0</code>.
*/
@Pure
public static int getAttributeIntWithDefault(Node document, int defaultValue, String... path) {
return getAttributeIntWithDefault(document, true, defaultValue, path);
}
/** Replies the long value that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
*
* @param document is the XML document to explore.
* @param caseSensitive indicates of the {@code path}'s components are case sensitive.
* @param path is the list of and ended by the attribute's name.
* @return the long value of the specified attribute or <code>null</code> if
* it was node found in the document
*/
@Pure
public static long getAttributeLong(Node document, boolean caseSensitive, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getAttributeLongWithDefault(document, caseSensitive, 0, path);
}
/** Replies the long value that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
* Be careful about the fact that the names are case sensitives.
*
* @param document is the XML document to explore.
* @param path is the list of and ended by the attribute's name.
* @return the long value of the specified attribute or <code>0</code>.
*/
@Pure
public static long getAttributeLong(Node document, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getAttributeLongWithDefault(document, true, 0, path);
}
/** Replies the long value that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
*
* @param document is the XML document to explore.
* @param caseSensitive indicates of the {@code path}'s components are case sensitive.
* @param defaultValue is the default value to reply.
* @param path is the list of and ended by the attribute's name.
* @return the long value of the specified attribute or <code>null</code> if
* it was node found in the document
*/
@Pure
public static long getAttributeLongWithDefault(Node document, boolean caseSensitive, long defaultValue, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
final String v = getAttributeValue(document, caseSensitive, 0, path);
if (v != null) {
try {
return Long.parseLong(v);
} catch (NumberFormatException e) {
//
}
}
return defaultValue;
}
/** Replies the long value that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
* Be careful about the fact that the names are case sensitives.
*
* @param document is the XML document to explore.
* @param defaultValue is the default value to reply.
* @param path is the list of and ended by the attribute's name.
* @return the long value of the specified attribute or <code>0</code>.
*/
@Pure
public static long getAttributeLongWithDefault(Node document, long defaultValue, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getAttributeLongWithDefault(document, true, defaultValue, path);
}
/** Replies the URL that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
*
* @param document is the XML document to explore.
* @param caseSensitive indicates of the {@code path}'s components are case sensitive.
* @param path is the list of and ended by the attribute's name.
* @return the URL in the specified attribute or <code>null</code> if
* it was node found in the document
*/
@Pure
public static URL getAttributeURL(Node document, boolean caseSensitive, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getAttributeURLWithDefault(document, caseSensitive, null, path);
}
/** Replies the URL that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
* Be careful about the fact that the names are case sensitives.
*
* @param document is the XML document to explore.
* @param path is the list of and ended by the attribute's name.
* @return the URL in the specified attribute or <code>null</code> if
* it was node found in the document
*/
@Pure
public static URL getAttributeURL(Node document, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getAttributeURLWithDefault(document, true, null, path);
}
/** Replies the URL that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
*
* @param document is the XML document to explore.
* @param caseSensitive indicates of the {@code path}'s components are case sensitive.
* @param defaultValue is the default value to reply.
* @param path is the list of and ended by the attribute's name.
* @return the URL in the specified attribute or <code>null</code> if
* it was node found in the document
*/
@Pure
public static URL getAttributeURLWithDefault(Node document, boolean caseSensitive, URL defaultValue, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
final String v = getAttributeValue(document, caseSensitive, 0, path);
if (v != null && !v.isEmpty()) {
final URL url = FileSystem.convertStringToURL(v, true);
if (url != null) {
return url;
}
}
return defaultValue;
}
/** Replies the URL that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
* Be careful about the fact that the names are case sensitives.
*
* @param document is the XML document to explore.
* @param defaultValue is the default value to reply.
* @param path is the list of and ended by the attribute's name.
* @return the URL in the specified attribute or <code>null</code> if
* it was node found in the document
*/
@Pure
public static URL getAttributeURLWithDefault(Node document, URL defaultValue, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getAttributeURLWithDefault(document, true, defaultValue, path);
}
/** Replies the UUID that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
*
* @param document is the XML document to explore.
* @param caseSensitive indicates of the {@code path}'s components are case sensitive.
* @param path is the list of and ended by the attribute's name.
* @return the UUID in the specified attribute or <code>null</code> if
* it was node found in the document
*/
@Pure
public static UUID getAttributeUUID(Node document, boolean caseSensitive, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getAttributeUUIDWithDefault(document, caseSensitive, null, path);
}
/** Replies the UUID that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
* Be careful about the fact that the names are case sensitives.
*
* @param document is the XML document to explore.
* @param path is the list of and ended by the attribute's name.
* @return the UUID in the specified attribute or <code>null</code> if
* it was node found in the document
*/
@Pure
public static UUID getAttributeUUID(Node document, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getAttributeUUIDWithDefault(document, true, null, path);
}
/** Replies the UUIDs that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
*
* @param document is the XML document to explore.
* @param caseSensitive indicates of the {@code path}'s components are case sensitive.
* @param path is the list of and ended by the attribute's name.
* @return the UUIDs in the specified attribute, never <code>null</code>
*/
@Pure
public static List<UUID> getAttributeUUIDs(Node document, boolean caseSensitive, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
final List<UUID> ids = new ArrayList<>();
final String v = getAttributeValue(document, caseSensitive, 0, path);
if (v != null && !v.isEmpty()) {
for (final String id : v.split(COLUMN_SEPARATOR)) {
try {
ids.add(UUID.fromString(id));
} catch (Exception e) {
//
}
}
}
return ids;
}
/** Replies the UUIDs that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
* Be careful about the fact that the names are case sensitives.
*
* @param document is the XML document to explore.
* @param path is the list of and ended by the attribute's name.
* @return the UUIDs in the specified attribute, never <code>null</code>
*/
@Pure
public static List<UUID> getAttributeUUIDs(Node document, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getAttributeUUIDs(document, true, path);
}
/** Replies the UUID that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
*
* @param document is the XML document to explore.
* @param caseSensitive indicates of the {@code path}'s components are case sensitive.
* @param defaultValue is the default value to reply.
* @param path is the list of and ended by the attribute's name.
* @return the UUID in the specified attribute or <code>null</code> if
* it was node found in the document
*/
@Pure
public static UUID getAttributeUUIDWithDefault(Node document, boolean caseSensitive, UUID defaultValue, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
final String v = getAttributeValue(document, caseSensitive, 0, path);
if (v != null && !v.isEmpty()) {
try {
final UUID id = UUID.fromString(v);
if (id != null) {
return id;
}
} catch (Exception e) {
//
}
}
return defaultValue;
}
/** Replies the UUID that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
* Be careful about the fact that the names are case sensitives.
*
* @param document is the XML document to explore.
* @param defaultValue is the default value to reply.
* @param path is the list of and ended by the attribute's name.
* @return the UUID in the specified attribute or <code>null</code> if
* it was node found in the document
*/
@Pure
public static UUID getAttributeUUIDWithDefault(Node document, UUID defaultValue, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getAttributeUUIDWithDefault(document, true, defaultValue, path);
}
/** Replies the value that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
*
* @param document is the XML document to explore.
* @param caseSensitive indicates of the {@code path}'s components are case sensitive.
* @param idxStart is the index of the first element of the path to use.
* @param path is the list of and ended by the attribute's name.
* @return the value of the specified attribute or <code>null</code> if
* it was node found in the document
*/
@Pure
@SuppressWarnings("checkstyle:cyclomaticcomplexity")
private static String getAttributeValue(Node document, boolean caseSensitive, int idxStart, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
assert path != null && (path.length - idxStart) >= 0 : AssertMessages.invalidValue(2);
if ((path.length - idxStart) > 1) {
final NodeList nodes = document.getChildNodes();
final int len = nodes.getLength();
for (int i = 0; i < len; ++i) {
final Node node = nodes.item(i);
if (node != null) {
final String name = node.getNodeName();
if (name != null
&& ((caseSensitive && name.equals(path[idxStart]))
|| (!caseSensitive && name.equalsIgnoreCase(path[idxStart])))) {
final String value = getAttributeValue(node, caseSensitive, idxStart + 1, path);
if (value != null) {
return value;
}
}
}
}
} else if (document instanceof Element) {
return ((Element) document).getAttribute(path[idxStart]);
} else {
final NamedNodeMap attrs = document.getAttributes();
if (attrs != null) {
final int len = attrs.getLength();
for (int idxAttr = 0; idxAttr < len; ++idxAttr) {
final Node node = attrs.item(idxAttr);
final String name = node.getNodeName();
if (name != null
&& ((caseSensitive && name.equals(path[idxStart]))
|| (!caseSensitive && name.equalsIgnoreCase(path[idxStart])))) {
return node.getNodeValue();
}
}
}
}
return null;
}
/** Replies the value that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
*
* @param document is the XML document to explore.
* @param casesSensitive indicates of the {@code path}'s components are case sensitive.
* @param path is the list of and ended by the attribute's name.
* @return the value of the specified attribute or <code>null</code> if
* it was node found in the document
*/
@Pure
public static String getAttributeValue(Node document, boolean casesSensitive, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getAttributeValue(document, casesSensitive, 0, path);
}
/** Replies the value that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
* Be careful about the fact that the names are case sensitives.
*
* @param document is the XML document to explore.
* @param path is the list of and ended by the attribute's name.
* @return the value of the specified attribute or <code>null</code> if
* it was node found in the document
*/
@Pure
public static String getAttributeValue(Node document, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getAttributeValue(document, true, 0, path);
}
/** Replies the value that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
*
* @param document is the XML document to explore.
* @param caseSensitive indicates of the {@code path}'s components are case sensitive.
* @param defaultValue is the default value to reply if no attribute value was found.
* @param path is the list of and ended by the attribute's name.
* @return the value of the specified attribute or <code>null</code> if
* it was node found in the document
*/
@Pure
public static String getAttributeValueWithDefault(Node document, boolean caseSensitive, String defaultValue,
String... path) {
assert document != null : AssertMessages.notNullParameter(0);
final String v = getAttributeValue(document, caseSensitive, 0, path);
if (v != null && !v.isEmpty()) {
return v;
}
return defaultValue;
}
/** Replies the value that corresponds to the specified attribute's path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the attribute.
*
* @param document is the XML document to explore.
* @param defaultValue is the default value to reply if no attribute value was found.
* @param path is the list of and ended by the attribute's name.
* @return the value of the specified attribute or <code>null</code> if
* it was node found in the document
*/
@Pure
public static String getAttributeValueWithDefault(Node document, String defaultValue, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getAttributeValueWithDefault(document, true, defaultValue, path);
}
/** Replies the first child node that has the specified type.
*
* @param <T> is the type of the desired child
* @param parent is the element from which the child must be extracted.
* @param type is the type of the desired child
* @return the child node or <code>null</code> if none.
*/
@Pure
public static <T extends Node> T getChild(Node parent, Class<T> type) {
assert parent != null : AssertMessages.notNullParameter(0);
assert type != null : AssertMessages.notNullParameter(1);
final NodeList children = parent.getChildNodes();
final int len = children.getLength();
for (int i = 0; i < len; ++i) {
final Node child = children.item(i);
if (type.isInstance(child)) {
return type.cast(child);
}
}
return null;
}
/** Replies the XML Document that is containing the given node.
*
* @param node the node.
* @return the Document in which the given node is, or <code>null</code>
* if not found.
*/
@Pure
public static Document getDocumentFor(Node node) {
Node localnode = node;
while (localnode != null) {
if (localnode instanceof Document) {
return (Document) localnode;
}
localnode = localnode.getParentNode();
}
return null;
}
/** Replies the node that corresponds to the specified path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the desired node.
*
* @param document is the XML document to explore.
* @param caseSensitive indicates of the {@code path}'s components are case sensitive.
* @param idxStart is the index of the first element of the path to use.
* @param path is the list of names.
* @return the node or <code>null</code> if
* it was not found in the document.
*/
@Pure
private static Element getElementFromPath(Node document, boolean caseSensitive, int idxStart, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
if (path != null && (path.length - idxStart) >= 1) {
final NodeList nodes = document.getChildNodes();
final int len = nodes.getLength();
for (int i = 0; i < len; ++i) {
final Node node = nodes.item(i);
if (node instanceof Element) {
final Element element = (Element) node;
final String name = node.getNodeName();
if (name != null
&& ((caseSensitive && name.equals(path[idxStart]))
|| (!caseSensitive && name.equalsIgnoreCase(path[idxStart])))) {
final Element nd = (path.length - idxStart) == 1
? element : getElementFromPath(node, caseSensitive, idxStart + 1, path);
if (nd != null) {
return nd;
}
}
}
}
}
return null;
}
/** Replies the node that corresponds to the specified path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the desired node.
*
* @param document is the XML document to explore.
* @param caseSensitive indicates of the {@code path}'s components are case sensitive.
* @param path is the list of names.
* @return the node or <code>null</code> if it was not found in the document.
*/
@Pure
public static Element getElementFromPath(Node document, boolean caseSensitive, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getElementFromPath(document, caseSensitive, 0, path);
}
/** Replies the node that corresponds to the specified path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the desired node.
* Be careful about the fact that the names are case sensitives.
*
* @param document is the XML document to explore.
* @param path is the list of names.
* @return the node or <code>null</code> if it was not found in the document.
*/
@Pure
public static Element getElementFromPath(Node document, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getElementFromPath(document, true, 0, path);
}
/** Replies the node that corresponds to the specified path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the desired node.
*
* @param document is the XML document to explore.
* @param constraint is the constraint that the replied element must respect.
* @param caseSensitive indicates of the {@code path}'s components are case sensitive.
* @param path is the list of names.
* @return the node or <code>null</code> if it was not found in the document.
*/
@Pure
public static Element getElementMatching(Node document, XMLConstraint constraint, boolean caseSensitive, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
assert constraint != null : AssertMessages.notNullParameter(1);
for (final Element element : getElementsFromPath(document, caseSensitive, path)) {
if (constraint.isValidElement(element)) {
return element;
}
}
return null;
}
/** Replies the node that corresponds to the specified path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the desired node.
*
* @param document is the XML document to explore.
* @param constraint is the constraint that the replied element must respect.
* @param path is the list of names.
* @return the node or <code>null</code> if it was not found in the document.
*/
@Pure
public static Element getElementMatching(Node document, XMLConstraint constraint, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
assert constraint != null : AssertMessages.notNullParameter(1);
return getElementMatching(document, constraint, true, path);
}
/** Replies the node that corresponds to the specified path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the desired node.
*
* @param document is the XML document to explore.
* @param caseSensitive indicates of the {@code path}'s components are case sensitive.
* @param idxStart is the index of the first element of the path to use.
* @param result is the node list to fill.
* @param path is the list of names.
*/
private static void getElementsFromPath(Node document, boolean caseSensitive, int idxStart,
List<Element> result, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
if (path != null && (path.length - idxStart) >= 1) {
final NodeList nodes = document.getChildNodes();
final int len = nodes.getLength();
for (int i = 0; i < len; ++i) {
final Node node = nodes.item(i);
if (node instanceof Element) {
final Element element = (Element) node;
final String name = element.getNodeName();
if (name != null
&& ((caseSensitive && name.equals(path[idxStart]))
|| (!caseSensitive && name.equalsIgnoreCase(path[idxStart])))) {
if ((path.length - idxStart) == 1) {
result.add(element);
} else {
getElementsFromPath(element, caseSensitive, idxStart + 1, result, path);
}
}
}
}
}
}
/** Replies the node that corresponds to the specified path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the desired node.
*
* @param document is the XML document to explore.
* @param caseSensitive indicates of the {@code path}'s components are case sensitive.
* @param path is the list of names.
* @return the node or <code>null</code> if it was not found in the document.
*/
@Pure
public static List<Element> getElementsFromPath(Node document, boolean caseSensitive, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
final List<Element> list = new ArrayList<>();
getElementsFromPath(document, caseSensitive, 0, list, path);
return list;
}
/** Replies the node that corresponds to the specified path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the desired node.
* Be careful about the fact that the names are case sensitives.
*
* @param document is the XML document to explore.
* @param path is the list of names.
* @return the node or <code>null</code> if it was not found in the document.
*/
@Pure
public static List<Element> getElementsFromPath(Node document, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
final List<Element> list = new ArrayList<>();
getElementsFromPath(document, true, 0, list, path);
return list;
}
/** Replies the node that corresponds to the specified path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the desired node.
*
* @param document is the XML document to explore.
* @param caseSensitive indicates of the {@code path}'s components are case sensitive.
* @param idxStart is the index of the first element of the path to use.
* @param path is the list of names.
* @return the node or <code>null</code> if
* it was not found in the document.
*/
@Pure
private static Node getNodeFromPath(Node document, boolean caseSensitive, int idxStart, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
if (path != null && (path.length - idxStart) >= 1) {
final NodeList nodes = document.getChildNodes();
final int len = nodes.getLength();
for (int i = 0; i < len; ++i) {
final Node node = nodes.item(i);
if (node != null) {
final String name = node.getNodeName();
if (name != null
&& ((caseSensitive && name.equals(path[idxStart]))
|| (!caseSensitive && name.equalsIgnoreCase(path[idxStart])))) {
final Node nd = (path.length - idxStart) == 1
? node : getNodeFromPath(node, caseSensitive, idxStart + 1, path);
if (nd != null) {
return nd;
}
}
}
}
}
return null;
}
/** Replies the node that corresponds to the specified path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the desired node.
*
* @param document is the XML document to explore.
* @param caseSensitive indicates of the {@code path}'s components are case sensitive.
* @param path is the list of names.
* @return the node or <code>null</code> if
* it was not found in the document.
*/
@Pure
public static Node getNodeFromPath(Node document, boolean caseSensitive, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getNodeFromPath(document, caseSensitive, 0, path);
}
/** Replies the node that corresponds to the specified path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the desired node.
* Be careful about the fact that the names are case sensitives.
*
* @param document is the XML document to explore.
* @param path is the list of names.
* @return the node or <code>null</code> if
* it was not found in the document.
*/
@Pure
public static Node getNodeFromPath(Node document, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return getNodeFromPath(document, true, 0, path);
}
/** Replies the node that corresponds to the specified path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the desired node.
*
* @param document is the XML document to explore.
* @param caseSensitive indicates of the {@code path}'s components are case sensitive.
* @param idxStart is the index of the first element of the path to use.
* @param result is the node list to fill.
* @param path is the list of names.
*/
private static void getNodesFromPath(Node document, boolean caseSensitive, int idxStart, List<Node> result, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
if (path != null && (path.length - idxStart) >= 1) {
final NodeList nodes = document.getChildNodes();
final int len = nodes.getLength();
for (int i = 0; i < len; ++i) {
final Node node = nodes.item(i);
if (node != null) {
final String name = node.getNodeName();
if (name != null
&& ((caseSensitive && name.equals(path[idxStart]))
|| (!caseSensitive && name.equalsIgnoreCase(path[idxStart])))) {
if ((path.length - idxStart) == 1) {
result.add(node);
} else {
getNodesFromPath(node, caseSensitive, idxStart + 1, result, path);
}
}
}
}
}
}
/** Replies the node that corresponds to the specified path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the desired node.
*
* @param document is the XML document to explore.
* @param caseSensitive indicates of the {@code path}'s components are case sensitive.
* @param path is the list of names.
* @return the node or <code>null</code> if
* it was not found in the document.
*/
@Pure
public static List<Node> getNodesFromPath(Node document, boolean caseSensitive, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
final List<Node> list = new ArrayList<>();
getNodesFromPath(document, caseSensitive, 0, list, path);
return list;
}
/** Replies the node that corresponds to the specified path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the desired node.
* Be careful about the fact that the names are case sensitives.
*
* @param document is the XML document to explore.
* @param path is the list of names.
* @return the node or <code>null</code> if
* it was not found in the document.
*/
@Pure
public static List<Node> getNodesFromPath(Node document, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
final List<Node> list = new ArrayList<>();
getNodesFromPath(document, true, 0, list, path);
return list;
}
/** Replies the text inside the node at the specified path.
*
* <p>The path is an ordered list of tag's names and ended by the name of
* the desired node.
* Be careful about the fact that the names are case sensitives.
*
* @param document is the XML document to explore.
* @param path is the list of names. This path may be empty.
* @return the text or <code>null</code> if the node was not found in the document or the text
* inside was empty.
*/
@Pure
public static String getText(Node document, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
Node parentNode = getNodeFromPath(document, path);
if (parentNode == null) {
parentNode = document;
}
final StringBuilder text = new StringBuilder();
final NodeList children = parentNode.getChildNodes();
final int len = children.getLength();
for (int i = 0; i < len; ++i) {
final Node child = children.item(i);
if (child instanceof Text) {
text.append(((Text) child).getWholeText());
}
}
if (text.length() > 0) {
return text.toString();
}
return null;
}
/** Replies an iterator on nodes that have the specified node name.
*
* @param parent is the node from which the children must be extracted.
* @param nodeName is the name of the extracted nodes
* @return the iterator on the parents.
*/
@Pure
public static Iterator<Node> iterate(Node parent, String nodeName) {
assert parent != null : AssertMessages.notNullParameter(0);
assert nodeName != null && !nodeName.isEmpty() : AssertMessages.notNullParameter(0);
return new NameBasedIterator(parent, nodeName);
}
/**
* Parses an XML/HTML color.
*
* @param xmlColor is the color to translate from a XML/HTML string.
* @return the color.
* @throws ColorFormatException if the color has invalid format.
* @see #toColor(int, int, int, int)
*/
@Pure
public static int parseColor(String xmlColor) throws ColorFormatException {
if (xmlColor == null || "".equals(xmlColor)) { //$NON-NLS-1$
return 0;
}
if (xmlColor.startsWith("#")) { //$NON-NLS-1$
try {
final StringBuilder str = new StringBuilder(xmlColor);
str.deleteCharAt(0);
str.insert(0, "0x"); //$NON-NLS-1$
return decodeColor(str.toString());
} catch (NumberFormatException e) {
throw new ColorFormatException(xmlColor);
}
}
final Integer color = COLOR_MATCHES.get(xmlColor.toLowerCase());
if (color != null) {
return color.intValue();
}
throw new ColorFormatException(xmlColor);
}
/**
* Parses an XML/HTML date.
*
* @param xmlDate is the date to translate from a XML/HTML string.
* @return the date.
* @throws DateFormatException if the date has invalid format.
* @see #toString(Date)
*/
@Pure
public static Date parseDate(String xmlDate) throws DateFormatException {
if (xmlDate == null || xmlDate.isEmpty()) {
return null;
}
final SimpleDateFormat format = new SimpleDateFormat(DATE_FORMAT);
try {
return format.parse(xmlDate);
} catch (Exception e) {
//
}
throw new DateFormatException(xmlDate);
}
/** Deserialize an object from the given XML string.
*
* @param xmlSerializedObject is the string which is containing the serialized object.
* @return the serialized object extracted from the XML string.
* @throws IOException if something wrong append during deserialization.
* @throws ClassNotFoundException is thrown when the class for the deserialized object is not found.
*/
@Pure
public static Object parseObject(String xmlSerializedObject) throws IOException, ClassNotFoundException {
assert xmlSerializedObject != null : AssertMessages.notNullParameter(0);
try (ByteArrayInputStream bais = new ByteArrayInputStream(Base64Coder.decode(xmlSerializedObject))) {
final ObjectInputStream ois = new ObjectInputStream(bais);
return ois.readObject();
}
}
/** Parse a string with contains a set of bytes.
*
* @param text is the string to uudecode
* @return the decoding result
* @see #toString(byte[])
*/
@Pure
public static byte[] parseString(String text) {
return Base64Coder.decode(text);
}
/** Parse a string representation of an XML document.
*
* @param xmlString is the string representation of the XML document.
* @return the document or <code>null</code> in case of error.
*/
@Pure
public static Document parseXML(String xmlString) {
assert xmlString != null : AssertMessages.notNullParameter(0);
try {
return readXML(new StringReader(xmlString));
} catch (Exception e) {
//
}
return null;
}
/**
* Read an XML file and replies the DOM document.
*
* @param file is the file to read
* @return the DOM document red from the {@code file}.
* @throws IOException if the stream cannot be read.
* @throws SAXException if the stream does not contains valid XML data.
* @throws ParserConfigurationException if the parser cannot be configured.
*/
public static Document readXML(File file) throws IOException, SAXException, ParserConfigurationException {
assert file != null : AssertMessages.notNullParameter();
try (FileInputStream fis = new FileInputStream(file)) {
return readXML(fis);
}
}
/**
* Read an XML file and replies the DOM document.
*
* @param stream is the stream to read
* @return the DOM document red from the {@code file}.
* @throws IOException if the stream cannot be read.
* @throws SAXException if the stream does not contains valid XML data.
* @throws ParserConfigurationException if the parser cannot be configured.
*/
public static Document readXML(InputStream stream) throws IOException, SAXException, ParserConfigurationException {
assert stream != null : AssertMessages.notNullParameter();
try {
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
final DocumentBuilder builder = factory.newDocumentBuilder();
return builder.parse(stream);
} finally {
stream.close();
}
}
/**
* Read an XML file and replies the DOM document.
*
* @param reader is the stream to read
* @return the DOM document red from the {@code file}.
* @throws IOException if the stream cannot be read.
* @throws SAXException if the stream does not contains valid XML data.
* @throws ParserConfigurationException if the parser cannot be configured.
*/
public static Document readXML(Reader reader) throws IOException, SAXException, ParserConfigurationException {
assert reader != null : AssertMessages.notNullParameter();
try {
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
final DocumentBuilder builder = factory.newDocumentBuilder();
return builder.parse(new InputSource(reader));
} finally {
reader.close();
}
}
/**
* Read an XML file and replies the DOM document.
*
* @param file is the file to read
* @return the DOM document red from the {@code file}.
* @throws IOException if the stream cannot be read.
* @throws SAXException if the stream does not contains valid XML data.
* @throws ParserConfigurationException if the parser cannot be configured.
*/
public static Document readXML(String file) throws IOException, SAXException, ParserConfigurationException {
assert file != null : AssertMessages.notNullParameter();
try (FileInputStream fis = new FileInputStream(file)) {
return readXML(fis);
}
}
/**
* Read an XML file and replies the DOM document.
*
* @param file is the file to read
* @return the DOM document red from the {@code file}.
* @throws IOException if the stream cannot be read.
* @throws SAXException if the stream does not contains valid XML data.
* @throws ParserConfigurationException if the parser cannot be configured.
*/
public static Document readXML(URL file) throws IOException, SAXException, ParserConfigurationException {
assert file != null : AssertMessages.notNullParameter();
return readXML(file.openStream());
}
/**
* Read an XML fragment from an XML file.
*
* @param file is the file to read
* @return the fragment from the {@code file}.
* @throws IOException if the stream cannot be read.
* @throws SAXException if the stream does not contains valid XML data.
* @throws ParserConfigurationException if the parser cannot be configured.
*/
public static DocumentFragment readXMLFragment(File file) throws IOException, SAXException, ParserConfigurationException {
assert file != null : AssertMessages.notNullParameter();
try (FileInputStream fis = new FileInputStream(file)) {
return readXMLFragment(fis);
}
}
/**
* Read an XML fragment from an XML file.
*
* @param stream is the stream to read
* @return the fragment from the {@code stream}.
* @throws IOException if the stream cannot be read.
* @throws SAXException if the stream does not contains valid XML data.
* @throws ParserConfigurationException if the parser cannot be configured.
*/
public static DocumentFragment readXMLFragment(InputStream stream) throws IOException,
SAXException, ParserConfigurationException {
assert stream != null : AssertMessages.notNullParameter();
try {
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
final DocumentBuilder builder = factory.newDocumentBuilder();
final Document doc = builder.parse(stream);
final DocumentFragment fragment = doc.createDocumentFragment();
final NodeList children = doc.getChildNodes();
final int length = children.getLength();
for (int i = 0; i < length; ++i) {
fragment.appendChild(children.item(i));
}
return fragment;
} finally {
stream.close();
}
}
/**
* Read an XML fragment from an XML file.
*
* @param reader is the stream to read
* @return the fragment from the {@code reader}.
* @throws IOException if the stream cannot be read.
* @throws SAXException if the stream does not contains valid XML data.
* @throws ParserConfigurationException if the parser cannot be configured.
*/
public static DocumentFragment readXMLFragment(Reader reader) throws IOException, SAXException,
ParserConfigurationException {
assert reader != null : AssertMessages.notNullParameter();
try {
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
final DocumentBuilder builder = factory.newDocumentBuilder();
final Document doc = builder.parse(new InputSource(reader));
final DocumentFragment fragment = doc.createDocumentFragment();
final NodeList children = doc.getChildNodes();
final int len = children.getLength();
for (int i = 0; i < len; ++i) {
fragment.appendChild(children.item(i));
}
return fragment;
} finally {
reader.close();
}
}
/**
* Read an XML fragment from an XML file.
*
* @param file is the file to read
* @return the fragment from the {@code file}.
* @throws IOException if the stream cannot be read.
* @throws SAXException if the stream does not contains valid XML data.
* @throws ParserConfigurationException if the parser cannot be configured.
*/
public static DocumentFragment readXMLFragment(String file) throws IOException, SAXException, ParserConfigurationException {
assert file != null : AssertMessages.notNullParameter();
try (FileInputStream fis = new FileInputStream(file)) {
return readXMLFragment(fis);
}
}
/**
* Read an XML fragment from an XML file.
*
* @param file is the file to read
* @return the fragment from the {@code file}.
* @throws IOException if the stream cannot be read.
* @throws SAXException if the stream does not contains valid XML data.
* @throws ParserConfigurationException if the parser cannot be configured.
*/
public static DocumentFragment readXMLFragment(URL file) throws IOException, SAXException, ParserConfigurationException {
assert file != null : AssertMessages.notNullParameter();
return readXMLFragment(file.openStream());
}
/** Write an enumeration value.
*
* @param <T> is the type of the enumeration.
* @param document is the XML document to explore.
* @param type is the type of the enumeration.
* @param caseSensitive indicates of the {@code path}'s components are case sensitive.
* @param value is the value to put in the document.
* @param path is the list of and ended by the attribute's name.
* @return <code>true</code> if written, <code>false</code> if not written.
*/
public static <T extends Enum<T>> boolean setAttributeEnum(Node document, Class<T> type,
boolean caseSensitive, T value, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
assert type != null : AssertMessages.notNullParameter(1);
if (value != null) {
final String[] thePath = Arrays.copyOf(path, path.length - 1);
final String attrName = path[path.length - 1];
if (attrName != null && !attrName.isEmpty()) {
Element node = null;
if (thePath != null && thePath.length > 0) {
node = getElementFromPath(document, caseSensitive, 0, thePath);
} else if (document instanceof Element) {
node = (Element) document;
}
if (node != null) {
node.setAttribute(attrName, value.name());
return true;
}
}
}
return false;
}
/** Write an enumeration value.
*
* @param <T> is the type of the enumeration.
* @param document is the XML document to explore.
* @param type is the type of the enumeration.
* @param value is the value to put in the document.
* @param path is the list of and ended by the attribute's name.
* @return <code>true</code> if written, <code>false</code> if not written.
*/
public static <T extends Enum<T>> boolean setAttributeEnum(Node document, Class<T> type, T value, String... path) {
assert document != null : AssertMessages.notNullParameter(0);
return setAttributeEnum(document, type, true, value, path);
}
/**
* Replies an XML/HTML color.
*
* @param rgba the color components.
* @return the XML color encoding.
* @see #parseColor(String)
*/
@Pure
@SuppressWarnings("checkstyle:magicnumber")
public static String toColor(int rgba) {
final StringBuilder s = new StringBuilder("#"); //$NON-NLS-1$
s.append(Integer.toHexString(rgba));
while (s.length() < 7) {
s.insert(1, '0');
}
return s.toString();
}
/**
* Replies an XML/HTML color.
*
* @param red the red component.
* @param green the green component.
* @param blue the blue component.
* @param alpha the alpha component.
* @return the XML color encoding.
* @see #parseColor(String)
*/
@Pure
public static String toColor(int red, int green, int blue, int alpha) {
return toColor(encodeColor(red, green, blue, alpha));
}
/** Translate a set of bytes into an XML-compliant set of characters.
*
* @param array is the string to uuencode.
* @return the string representation of the given array.
* @see #parseString(String)
*/
@Pure
public static String toString(byte[] array) {
assert array != null : AssertMessages.notNullParameter(0);
return new String(Base64Coder.encode(array));
}
/**
* Replies an XML/HTML date.
*
* @param date is the date to translate into a XML/HTML string.
* @return the XML date encoding.
* @see #parseDate(String)
*/
@Pure
public static String toString(Date date) {
if (date == null) {
return ""; //$NON-NLS-1$
}
final SimpleDateFormat f = new SimpleDateFormat(DATE_FORMAT);
return f.format(date);
}
/** Translate the XML tree into a string representation.
*
* @param node is the object that contains the node tree
* @return the string representation or <code>null</code> in case of error.
*/
@Pure
public static String toString(Node node) {
assert node != null : AssertMessages.notNullParameter(0);
try (StringWriter writer = new StringWriter()) {
writeXML(node, writer);
return writer.toString();
} catch (Exception e) {
//
}
return null;
}
/** Serialize the given object and put it in a string which is compliant with XML format.
*
* @param object is the object to serialize.
* @return the XML representation of the object.
* @throws IOException if something wrong append during serialization.
*/
@Pure
public static String toString(Serializable object) throws IOException {
assert object != null : AssertMessages.notNullParameter(0);
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
final ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(object);
return new String(Base64Coder.encode(baos.toByteArray()));
}
}
private static void writeNode(Node node, OutputStream stream) throws IOException {
assert node != null : AssertMessages.notNullParameter(0);
assert stream != null : AssertMessages.notNullParameter(1);
try {
final TransformerFactory transFactory = TransformerFactory.newInstance();
transFactory.setAttribute(INDENT_NUMBER, Integer.valueOf(2));
final Transformer trans = transFactory.newTransformer();
trans.setParameter(OutputKeys.INDENT, CONSTANT_YES);
final DOMSource source = new DOMSource(node);
try (PrintStream flot = new PrintStream(stream)) {
final StreamResult xmlStream = new StreamResult(flot);
trans.transform(source, xmlStream);
}
} catch (Exception e) {
throw new IOException(e);
}
}
private static void writeNode(Node node, Writer writer) throws IOException {
assert node != null : AssertMessages.notNullParameter(0);
assert writer != null : AssertMessages.notNullParameter(1);
try {
final TransformerFactory transFactory = TransformerFactory.newInstance();
transFactory.setAttribute(INDENT_NUMBER, Integer.valueOf(2));
final Transformer trans = transFactory.newTransformer();
trans.setParameter(OutputKeys.INDENT, CONSTANT_YES);
final DOMSource source = new DOMSource(node);
try (PrintWriter flot = new PrintWriter(writer)) {
final StreamResult xmlStream = new StreamResult(flot);
trans.transform(source, xmlStream);
}
} catch (Exception e) {
throw new IOException(e);
}
}
/** Write the given node tree into a XML file.
*
* @param xmldocument is the object that contains the node tree
* @param file is the file name.
* @throws IOException if the stream cannot be read.
* @throws ParserConfigurationException if the parser cannot be configured.
*/
public static void writeXML(Document xmldocument, File file) throws ParserConfigurationException, IOException {
assert xmldocument != null : AssertMessages.notNullParameter();
assert file != null : AssertMessages.notNullParameter();
try (FileOutputStream fos = new FileOutputStream(file)) {
writeNode(xmldocument, fos);
}
}
/** Write the given node tree into a XML file.
*
* @param xmldocument is the object that contains the node tree
* @param stream is the target stream
* @throws IOException if the stream cannot be read.
*/
public static void writeXML(Document xmldocument, OutputStream stream) throws IOException {
assert xmldocument != null : AssertMessages.notNullParameter(0);
assert stream != null : AssertMessages.notNullParameter(1);
writeNode(xmldocument, stream);
}
/** Write the given node tree into a XML file.
*
* @param xmldocument is the object that contains the node tree
* @param file is the file name.
* @throws IOException if the stream cannot be read.
* @throws ParserConfigurationException if the parser cannot be configured.
*/
public static void writeXML(Document xmldocument, String file) throws ParserConfigurationException, IOException {
assert file != null : AssertMessages.notNullParameter();
try (FileOutputStream fos = new FileOutputStream(file)) {
writeNode(xmldocument, fos);
}
}
/** Write the given node tree into a XML file.
*
* @param xmldocument is the object that contains the node tree
* @param writer is the target stream
* @throws IOException if the stream cannot be read.
*/
public static void writeXML(Document xmldocument, Writer writer) throws IOException {
assert xmldocument != null : AssertMessages.notNullParameter(0);
assert writer != null : AssertMessages.notNullParameter(1);
writeNode(xmldocument, writer);
}
/** Write the given node tree into a XML file.
*
* @param fragment is the object that contains the node tree
* @param file is the file name.
* @throws IOException if the stream cannot be read.
* @throws ParserConfigurationException if the parser cannot be configured.
*/
public static void writeXML(DocumentFragment fragment, File file) throws ParserConfigurationException, IOException {
assert fragment != null : AssertMessages.notNullParameter(0);
assert file != null : AssertMessages.notNullParameter(1);
try (FileOutputStream fos = new FileOutputStream(file)) {
writeNode(fragment, fos);
}
}
/** Write the given node tree into a XML file.
*
* @param fragment is the object that contains the node tree
* @param stream is the target stream
* @throws IOException if the stream cannot be read.
*/
public static void writeXML(DocumentFragment fragment, OutputStream stream) throws IOException {
assert fragment != null : AssertMessages.notNullParameter(0);
assert stream != null : AssertMessages.notNullParameter(1);
writeNode(fragment, stream);
}
/** Write the given node tree into a XML file.
*
* @param fragment is the object that contains the node tree
* @param file is the file name.
* @throws IOException if the stream cannot be read.
* @throws ParserConfigurationException if the parser cannot be configured.
*/
public static void writeXML(DocumentFragment fragment, String file) throws ParserConfigurationException, IOException {
assert fragment != null : AssertMessages.notNullParameter(0);
assert file != null : AssertMessages.notNullParameter(1);
try (FileOutputStream fos = new FileOutputStream(file)) {
writeNode(fragment, fos);
}
}
/** Write the given node tree into a XML file.
*
* @param fragment is the object that contains the node tree
* @param writer is the target stream
* @throws IOException if the stream cannot be read.
*/
public static void writeXML(DocumentFragment fragment, Writer writer) throws IOException {
assert fragment != null : AssertMessages.notNullParameter(0);
assert writer != null : AssertMessages.notNullParameter(1);
writeNode(fragment, writer);
}
/** Write the given node tree into a XML file.
*
* @param node is the object that contains the node tree
* @param stream is the target stream
* @throws IOException if the stream cannot be read.
*/
public static void writeXML(Node node, OutputStream stream) throws IOException {
writeNode(node, stream);
}
/** Write the given node tree into a XML file.
*
* @param node is the object that contains the node tree
* @param writer is the target stream
* @throws IOException if the writer cannot be used.
*/
public static void writeXML(Node node, Writer writer) throws IOException {
writeNode(node, writer);
}
/**
* Utility class for manipulating XML data and files.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
* @since 14.0
*/
private static class NameBasedIterator implements Iterator<Node> {
private final String nodeName;
private final NodeList list;
private Node next;
private int index;
/**
* @param parent the parent node.
* @param nodeName the child node name.
*/
NameBasedIterator(Node parent, String nodeName) {
this.index = 0;
this.list = parent.getChildNodes();
this.nodeName = nodeName;
searchNode();
}
@Pure
@Override
public boolean hasNext() {
return this.next != null;
}
@Override
public Node next() {
final Node child = this.next;
if (child == null) {
throw new NoSuchElementException();
}
searchNode();
return child;
}
private void searchNode() {
this.next = null;
while (this.index < this.list.getLength()) {
final Node child = this.list.item(this.index);
++this.index;
if (this.nodeName.equals(child.getNodeName())) {
this.next = child;
return;
}
}
}
}
}