All Downloads are FREE. Search and download functionalities are using the official Maven repository.

eu.europa.esig.dss.XmlDom Maven / Gradle / Ivy

Go to download

DSS Service Provider Interface contains the contract interface shared between the applet and the server-side of DSS.

There is a newer version: 5.4.d4j.1
Show newest version
/**
 * DSS - Digital Signature Services
 * Copyright (C) 2015 European Commission, provided under the CEF programme
 *
 * This file is part of the "DSS - Digital Signature Services" project.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package eu.europa.esig.dss;

import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

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 javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * This class encapsulates an org.w3c.dom.Document. Its integrates the ability to execute XPath queries on XML
 * documents.
 */
public class XmlDom {

	private static final Logger LOG = LoggerFactory.getLogger(XmlDom.class);

	public static final String NAMESPACE = "http://dss.esig.europa.eu/validation/diagnostic";

	private static final String NS_PREFIX = "dss";

	private static final XPathFactory factory = XPathFactory.newInstance();

	private static final NamespaceContextMap nsContext;

	private static final Map namespaces;

	static {

		namespaces = new HashMap();
		namespaces.put(NS_PREFIX, NAMESPACE);
		nsContext = new NamespaceContextMap();
		nsContext.registerNamespace(NS_PREFIX, NAMESPACE);
	}

	public final Element rootElement;

	String nameSpace;

	public XmlDom(final Document document) {

		this.rootElement = document.getDocumentElement();
		nameSpace = rootElement.getNamespaceURI();
	}

	public XmlDom(final Element element) {

		this.rootElement = element;
	}

	private static XPathExpression createXPathExpression(final String xpathString) {

		final XPath xpath = factory.newXPath();
		xpath.setNamespaceContext(nsContext);
		try {

			final XPathExpression expr = xpath.compile(xpathString);
			return expr;
		} catch (XPathExpressionException ex) {

			throw new RuntimeException(ex);
		}
	}

	private static NodeList getNodeList(final Node xmlNode, final String xpathString) {

		try {

			final XPathExpression expr = createXPathExpression(xpathString);
			return (NodeList) expr.evaluate(xmlNode, XPathConstants.NODESET);
		} catch (XPathExpressionException e) {

			throw new RuntimeException(e);
		}
	}

	/**
	 * The list of elements corresponding the given XPath query and parameters.
	 *
	 * @param xPath
	 * @param params
	 * @return
	 */
	public List getElements(final String xPath, final Object... params) {

		try {

			String xPath_ = format(xPath, params);

			NodeList nodeList = getNodeList(rootElement, xPath_);
			List list = new ArrayList();
			for (int ii = 0; ii < nodeList.getLength(); ii++) {

				Node node = nodeList.item(ii);
				if ((node != null) && (node.getNodeType() == Node.ELEMENT_NODE)) {

					list.add(new XmlDom((Element) node));
				}
			}
			return list;
		} catch (Exception e) {

			String message = "XPath error: '" + xPath + "'.";
			throw new DSSException(message, e);
		}
	}

	public XmlDom getElement(final String xPath, final Object... params) {

		try {

			String xPath_ = format(xPath, params);

			NodeList nodeList = getNodeList(rootElement, xPath_);
			for (int ii = 0; ii < nodeList.getLength(); ii++) {

				Node node = nodeList.item(ii);
				if ((node != null) && (node.getNodeType() == Node.ELEMENT_NODE)) {

					return new XmlDom((Element) node);
				}
			}
			return null;
		} catch (Exception e) {

			String message = "XPath error: '" + xPath + "'.";
			throw new DSSException(message, e);
		}
	}

	/**
	 * @param xPath
	 * @param params
	 * @return
	 */
	private static String format(final String xPath, final Object... params) {

		String formattedXPath;
		if (params.length > 0) {

			formattedXPath = String.format(xPath, params);
		} else {

			formattedXPath = xPath;
		}
		formattedXPath = addNamespacePrefix(formattedXPath);
		return formattedXPath;
	}

	private static String addNamespacePrefix(final String formatedXPath) {

		if (formatedXPath.startsWith("/dss:") || formatedXPath.startsWith("./dss:")) {

			// Already formated.
			return formatedXPath;
		}
		String formatedXPath_ = formatedXPath;
		CharSequence from = "//";
		CharSequence to = "{#double}/";
		boolean special = formatedXPath_.indexOf("//") != -1;
		if (special) {
			formatedXPath_ = formatedXPath_.replace(from, to);
		}
		StringTokenizer tokenizer = new StringTokenizer(formatedXPath_, "/");

		StringBuilder stringBuilder = new StringBuilder();

		while (tokenizer.hasMoreTokens())

		{

			String token = tokenizer.nextToken();

			final boolean isDot = ".".equals(token);
			final boolean isCount = "count(".equals(token) || "count(.".equals(token);
			final boolean isDoubleDot = "..".equals(token);
			final boolean isAt = token.startsWith("@");
			final boolean isText = token.equals("text()");
			final boolean isDoubleSlash = token.equals("{#double}");
			final String slash = isDot || isCount || isDoubleSlash ? "" : "/";
			String prefix = isDot || isCount || isDoubleDot || isAt || isText || isDoubleSlash ? "" : "dss:";

			stringBuilder.append(slash).append(prefix).append(token);
		}

		String normalizedXPath = stringBuilder.toString();
		if (special) {
			normalizedXPath = normalizedXPath.replace(to, from);
		}
		return normalizedXPath;
	}

	/**
	 * This method never returns null.
	 *
	 * @param xPath
	 * @param params
	 * @return {@code String} value or empty string
	 */
	public String getValue(final String xPath, final Object... params) {

		String xPath_ = format(xPath, params);

		NodeList nodeList = getNodeList(rootElement, xPath_);
		if (nodeList.getLength() == 1) {
			Node node = nodeList.item(0);
			if (node.getNodeType() != Node.ELEMENT_NODE) {
				String value = nodeList.item(0).getTextContent();
				return value.trim();
			}
		}
		return "";
	}

	public int getIntValue(final String xPath, final Object... params) {

		String value = getValue(xPath, params);
		try {

			return Integer.parseInt(value);
		} catch (Exception e) {
			throw new DSSException(e);
		}
	}

	public long getLongValue(final String xPath, final Object... params) {

		String value = getValue(xPath, params);
		try {

			value = value.trim();
			return Long.parseLong(value);
		} catch (Exception e) {
			throw new DSSException(e);
		}
	}

	public boolean getBoolValue(final String xPath, final Object... params) {

		String value = getValue(xPath, params);
		if (value.equals("true")) {
			return true;

		} else if (value.isEmpty() || value.equals("false")) {

			return false;
		}
		throw new DSSException("Expected values are: true, false and not '" + value + "'.");
	}

	public long getCountValue(final String xPath, final Object... params) {

		String xpathString = format(xPath, params);
		try {

			XPathExpression xPathExpression = createXPathExpression(xpathString);
			Double number = (Double) xPathExpression.evaluate(rootElement, XPathConstants.NUMBER);
			return number.intValue();
		} catch (XPathExpressionException e) {

			throw new RuntimeException(e);
		}
	}

	public boolean exists(final String xPath, final Object... params) {

		XmlDom element = getElement(xPath, params);
		return element != null;
	}

	public Date getTimeValue(final String xPath, final Object... params) {

		String value = getValue(xPath, params);
		return DSSUtils.parseDate(value);
	}

	public Date getTimeValueOrNull(final String xPath, final Object... params) {

		String value = getValue(xPath, params);
		if (value.isEmpty()) {
			return null;
		}
		return DSSUtils.parseDate(value);
	}

	public String getText() {

		try {
			if (rootElement != null) {

				return rootElement.getTextContent().trim();
			}
		} catch (Exception e) {
		}
		return null;
	}

	/**
	 * The name of this node, depending on its type;
	 *
	 * @return
	 */
	public String getName() {

		return rootElement.getNodeName();
	}

	/**
	 * Retrieves an attribute value by name.
	 *
	 * @param attributeName
	 * @return
	 */
	public String getAttribute(final String attributeName) {

		return rootElement.getAttribute(attributeName);
	}

	/**
	 * Retrieves an attribute value by name.
	 *
	 * @return
	 */
	public NamedNodeMap getAttributes() {

		return rootElement.getAttributes();
	}

	/**
	 * Converts the list of {@code XmlDom} to {@code List} of {@code String}. The children of the node are not taken
	 * into account.
	 *
	 * @param xmlDomList the list of {@code XmlDom} to convert
	 * @return converted {@code List} of {@code String}.
	 */
	public static List convertToStringList(final List xmlDomList) {

		final List stringList = new ArrayList();
		for (final XmlDom xmlDom : xmlDomList) {

			stringList.add(xmlDom.getText());
		}
		return stringList;
	}

	/**
	 * Converts the list of {@code XmlDom} to {@code Map} of {@code String}, {@code String}. The children of the node are not taken
	 * into account.
	 *
	 * @param xmlDomList    the list of {@code XmlDom} to convert
	 * @param attributeName the name of the attribute to use as value
	 * @return converted {@code Map} of {@code String}, {@code String} corresponding to the element content and the attribute value.
	 */
	public static Map convertToStringMap(final List xmlDomList, final String attributeName) {

		final Map stringMap = new HashMap();
		for (final XmlDom xmlDom : xmlDomList) {

			final String key = xmlDom.getText();
			final String value = xmlDom.getAttribute(attributeName);
			stringMap.put(key, value);
		}
		return stringMap;
	}

	/**
	 * Converts the list of {@code XmlDom} to {@code Map} of {@code String}, {@code Date}. The children of the node are not taken
	 * into account. If a problem is encountered during the conversion the pair key, value is ignored and a warning is logged.
	 *
	 * @param xmlDomList    the list of {@code XmlDom} to convert
	 * @param attributeName the name of the attribute to use as value
	 * @return converted {@code Map} of {@code String}, {@code Date} corresponding to the element content and the attribute value.
	 */
	public static Map convertToStringDateMap(final List xmlDomList, final String attributeName) {

		final Map stringMap = new HashMap();
		for (final XmlDom xmlDom : xmlDomList) {

			final String key = xmlDom.getText();
			final String dateString = xmlDom.getAttribute(attributeName);
			String format = xmlDom.getAttribute("Format");
			if (StringUtils.isBlank(format)) {
				format = "yyyy-MM-dd";
			}
			if (StringUtils.isBlank(dateString)) {

				LOG.warn(String.format("The date is not defined for key '%s'!", key));
				continue;
			}
			final Date date;
			try {
				date = DSSUtils.parseDate(format, dateString);
			} catch (DSSException e) {

				LOG.warn("The date conversion is not possible.", e);
				continue;
			}
			stringMap.put(key, date);
		}
		return stringMap;
	}

	public byte[] toByteArray() {

		if (rootElement != null) {

			ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
			printDocument(rootElement, byteArrayOutputStream, false);
			return byteArrayOutputStream.toByteArray();
		}
		return DSSUtils.EMPTY_BYTE_ARRAY;
	}

	@Override
	public String toString() {

		if (rootElement != null) {

			ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
			printDocument(rootElement, byteArrayOutputStream, false);
			return getUtf8String(byteArrayOutputStream.toByteArray());
		}
		return super.toString();
	}


	/**
	 * Constructs a new String by decoding the specified array of bytes using the UTF-8 charset.
	 *
	 * @param bytes The bytes to be decoded into characters
	 * @return A new String decoded from the specified array of bytes using the UTF-8 charset,
	 * or null if the input byte array was null.
	 * @throws IllegalStateException Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen since the
	 *                               charset is required.
	 */
	private static String getUtf8String(byte[] bytes) {

		if (bytes == null) {
			return null;
		}
		try {
			return new String(bytes, "UTF-8");
		} catch (UnsupportedEncodingException e) {
			throw new DSSException(e);
		}
	}

	/**
	 * This method writes formatted {@link org.w3c.dom.Node} to the outputStream.
	 *
	 * @param node
	 * @param out
	 */
	private static void printDocument(final Node node, final OutputStream out, final boolean raw) {

		try {

			final TransformerFactory tf = TransformerFactory.newInstance();
			final Transformer transformer = tf.newTransformer();
			transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
			transformer.setOutputProperty(OutputKeys.METHOD, "xml");
			if (!raw) {

				transformer.setOutputProperty(OutputKeys.INDENT, "yes");
				transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "3");
			}
			transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");

			final DOMSource xmlSource = new DOMSource(node);
			final OutputStreamWriter writer = new OutputStreamWriter(out, "UTF-8");
			final StreamResult outputTarget = new StreamResult(writer);
			transformer.transform(xmlSource, outputTarget);
		} catch (Exception e) {
			throw new DSSException(e);
		}

	}

	public Element getRootElement() {
		return rootElement;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy