org.cristalise.kernel.persistency.outcome.Outcome Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cristalise-kernel Show documentation
Show all versions of cristalise-kernel Show documentation
Cristal-ise is a description-driven software platform originally developed to track the construction of
the CMS ECAL detector of the LHC at CERN. This is its core library, known as the kernel, which manages
business objects called Items. Items are entirely configured from data, called descriptions, held in other Items.
Every change of a state in an Item is a consequence of an execution of an activity in that Item's lifecycle,
meaning that Cristal-ise applications are completely traceable, even in their design. It also supports extensive
versioning of Item description data, giving the system a high level of flexibility.
/**
* This file is part of the CRISTAL-iSE kernel.
* Copyright (c) 2001-2015 The CRISTAL Consortium. All rights reserved.
*
* 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 3 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; with out 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*
* http://www.fsf.org/licensing/licenses/lgpl.html
*/
package org.cristalise.kernel.persistency.outcome;
import static org.cristalise.kernel.persistency.ClusterType.OUTCOME;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.StringTokenizer;
import javax.xml.namespace.QName;
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.TransformerConfigurationException;
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.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.apache.commons.lang3.StringUtils;
import org.cristalise.kernel.common.InvalidDataException;
import org.cristalise.kernel.common.ObjectNotFoundException;
import org.cristalise.kernel.common.PersistencyException;
import org.cristalise.kernel.entity.C2KLocalObject;
import org.cristalise.kernel.persistency.ClusterType;
import org.cristalise.kernel.process.Gateway;
import org.cristalise.kernel.utils.LocalObjectLoader;
import org.cristalise.kernel.utils.Logger;
import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.XMLUnit;
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;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
/**
* A C2KLocalObject encapsulating management of XML data. It has methods to manipulate and validate the XML,
* and with a valid ID it can be stored in ClusterStore.
*
* It contains lot of utility code to read and set data in the Outcome (xml).
*/
@Accessors(prefix = "m") @Getter @Setter
public class Outcome implements C2KLocalObject {
//These values are set in system properties to select more efficient xpath evaluation behaviour
private static final String DTM_MANAGER_NAME = "com.sun.org.apache.xml.internal.dtm.DTMManager";
private static final String DTM_MANAGER_VALUE = "com.sun.org.apache.xml.internal.dtm.ref.DTMManagerDefault";
private static final int NONE = -1;
/** ID is the eventID created when the Outcome is stored in History */
Integer mID;
/** The Schema object associated with the Outcome */
Schema mSchema;
/** The parsed XML document */
Document mDOM;
/** Parser of XML Documents */
static DocumentBuilder parser;
/** Use this static ThreadLocal variable for thread-safe XPath evaluation */
private static final ThreadLocal XPATH_FACTORY = new ThreadLocal() {
@Override
protected XPathFactory initialValue() {
return XPathFactory.newInstance();
}
};
static {
System.setProperty(DTM_MANAGER_NAME, DTM_MANAGER_VALUE);
// Set up parser
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setValidating(false);
dbf.setNamespaceAware(false);
try {
parser = dbf.newDocumentBuilder();
Logger.msg(7, "Outcome static class init: "+parser.getClass().getName());
}
catch (ParserConfigurationException e) {
Logger.error(e);
Logger.die("Cannot function without XML parser");
}
}
/**
* Use this constructor for XML manipulation only. This Outcome cannot be validate
* not it can be stored in ClusterStore.
*
* @param xml the XML string to be manipulated
* @throws InvalidDataException there was an error parsing the XML
*/
public Outcome(String xml) throws InvalidDataException {
this(NONE, xml, null);
}
/**
* Use this constructor for XML manipulation and validation. This Outcome cannot be stored in ClusterStore.
*
* @param xml the XML string to be manipulated
* @param schema the Schema of the XML
* @throws InvalidDataException there was an error parsing the XML
*/
public Outcome(String xml, Schema schema) throws InvalidDataException {
this(NONE, xml, schema);
}
/**
* Use this constructor to manipulate, validate and store this outcome
*
* @param id eventID
* @param xml the XML string to be manipulated
* @param schema the Schema of the XML
* @throws InvalidDataException there was an error parsing the XML
*/
public Outcome(int id, String xml, Schema schema) throws InvalidDataException {
this(id, (Document)null, schema);
try {
mDOM = parse(xml);
}
catch (IOException | SAXException ex) {
Logger.error("INVALID XML - schema:"+(null == mSchema ? null : mSchema.getName())+"\n"+xml);
Logger.error(ex);
throw new InvalidDataException("XML not valid for schema:"+mSchema+" error:"+ex.getMessage());
}
}
/**
* Very basic constructor to set all members
*
* @param id eventID
* @param dom parsed XML Document
* @param schema the Schema instance
*/
public Outcome(int id, Document dom, Schema schema) {
mID = id;
mDOM = dom;
mSchema = schema;
}
/**
* The constructor derives all the meta data (ID and Schema) from the path
*
* @param path the actuals path used by the ClusterStorage
* @param xml the XML string to parse
* @throws PersistencyException there was DB error
* @throws InvalidDataException Version or EventID was an invalid number
*/
public Outcome(String path, String xml) throws PersistencyException, InvalidDataException {
setMetaDataFromPath(path);
try {
mDOM = parse(xml);
}
catch (IOException | SAXException ex) {
Logger.error(ex);
throw new InvalidDataException("XML not valid: "+ex.getMessage());
}
}
/**
* The constructor derives all the meta data (ID and Schema) from the path
*
* @param path the actuals path used by the ClusterStorage
* @param data the parsed xml Document
* @throws PersistencyException there was DB error
* @throws InvalidDataException Version or EventID was an invalid number
*/
public Outcome(String path, Document data) throws PersistencyException, InvalidDataException {
setMetaDataFromPath(path);
mDOM = data;
}
/**
* Retrieves the SchemaName, Version, EevetnId triplet from the path. Check getClusterPath() implementation
*
* @param path the ClusterPath to work with
* @throws PersistencyException path was incorrect
* @throws InvalidDataException Schema was not found or the Path has incorrect data
*/
protected void setMetaDataFromPath(String path) throws PersistencyException, InvalidDataException {
StringTokenizer tok = new StringTokenizer(path,"/");
if (tok.countTokens() != 3 && !(tok.nextToken().equals(OUTCOME.getName())))
throw new PersistencyException("Outcome() - Outcome path must have three components:" + path);
String schemaName = tok.nextToken();
String verString = tok.nextToken();
String objId = tok.nextToken();
try {
Integer schemaVersion = Integer.valueOf(verString);
mSchema = LocalObjectLoader.getSchema(schemaName, schemaVersion);
mID = Integer.valueOf(objId);
}
catch (NumberFormatException ex) {
throw new InvalidDataException("Outcome() - Version or EventID was an invalid number version:"+verString + " eventID:" + objId);
}
catch (ObjectNotFoundException e) {
Logger.error(e);
throw new InvalidDataException("Outcome() - problem loading schema:"+schemaName+" version:"+verString);
}
}
/**
* Evaluates the given XPath expression thread-safely and efficiently
*
* @param xpathExpr the XPath exporession
* @return the result of the evaluated expression
* @throws XPathExpressionException If expression cannot be compiled.
*/
public Object evaluateXpath(String xpathExpr, QName returnType) throws XPathExpressionException {
XPath xpath = XPATH_FACTORY.get().newXPath();
return xpath.compile(xpathExpr).evaluate(mDOM, returnType);
}
/**
* Validates the actual XML Document against the provided Schema
*
* @return the errors found
* @throws InvalidDataException Schema was null
*/
public String validate() throws InvalidDataException {
if (mSchema == null) {
mDOM.normalize();
throw new InvalidDataException("Schema was NOT provided");
}
OutcomeValidator validator = OutcomeValidator.getValidator(mSchema);
if (Gateway.getProperties().getBoolean("Outcome.Validation.useDOM", true))
return validator.validate(mDOM);
else
return validator.validate(getData());
}
/**
* Validates the actual XML Document against the provided Schema
*
* @throws InvalidDataException XML document is not valid instance of the Schema
*/
public void validateAndCheck() throws InvalidDataException {
String error = validate();
if (StringUtils.isNotBlank(error)) {
Logger.error("Outcome.validateAndCheck() - Outcome not valid: " + error);
Logger.msg("XML: \n"+getData());
Logger.msg("XSD: \n"+getSchema().getXSD());
throw new InvalidDataException(error);
}
}
@Override
public void setName(String name) {
try {
mID = Integer.valueOf(name);
}
catch (NumberFormatException e) {
Logger.error("Invalid id set on Outcome:"+name);
}
}
@Override
public String getName() {
return mID.toString();
}
public void setData(String xml) throws SAXException, IOException {
mDOM = parse(xml);
}
/**
* Gets the value of the given TEXT, CDATA, ATTRIBUTE or ELEMENT Node
*
* @param node the Node to work on
* @return the value of the node
* @throws InvalidDataException the Node is not a proper type
*/
public String getNodeValue(Node node) throws InvalidDataException {
int type = node.getNodeType();
if (type == Node.TEXT_NODE || type == Node.CDATA_SECTION_NODE || type == Node.ATTRIBUTE_NODE) {
return node.getNodeValue();
}
else if (type == Node.ELEMENT_NODE) {
NodeList nodeChildren = node.getChildNodes();
if (nodeChildren.getLength() == 0) {
Logger.msg(5, "Outcome.getNodeValue() - No child/text node for node:"+node.getNodeName()+" => returning null");
//throw new InvalidDataException("No child/text node for element '"+node.getNodeName()+"'");
return null;
}
else if (nodeChildren.getLength() == 1) {
Node child = nodeChildren.item(0);
if (child.getNodeType() == Node.TEXT_NODE || child.getNodeType() == Node.CDATA_SECTION_NODE)
return child.getNodeValue();
else
throw new InvalidDataException("Node '"+node.getNodeName()+"' can't get data from child node name:"+child.getNodeName()+" type:"+type);
}
else
throw new InvalidDataException("Node '"+node.getNodeName()+"' shall have 0 or 1 children only #children:"+nodeChildren.getLength());
}
else
throw new InvalidDataException("Cannot handle node name:"+node.getNodeName()+" type:"+type);
}
/**
* Sets the value of the given TEXT, CDATA, ATTRIBUTE or ELEMENT Node
*
* @param node the Node to work on
* @param value the value to set
* @throws InvalidDataException the Node is not a proper type
*/
public void setNodeValue(Node node, String value) throws InvalidDataException {
setNodeValue(node, value, false);
}
/**
* Sets the value of the given TEXT, CDATA, ATTRIBUTE or ELEMENT Node
*
* @param node the Node to work on
* @param value the value to set
* @param useCdata force to use CDATA
* @throws InvalidDataException the Node is not a proper type
*/
public void setNodeValue(Node node, String value, boolean useCdata) throws InvalidDataException {
int type = node.getNodeType();
if (type == Node.TEXT_NODE || type == Node.CDATA_SECTION_NODE || type == Node.ATTRIBUTE_NODE) {
if (useCdata && type != Node.CDATA_SECTION_NODE )
throw new InvalidDataException("Node '"+node.getNodeName()+"' can't set cdata of attribute or text node");
node.setNodeValue(value);
}
else if (type == Node.ELEMENT_NODE) {
NodeList nodeChildren = node.getChildNodes();
if (nodeChildren.getLength() == 0) {
if (useCdata) node.appendChild(mDOM.createCDATASection(value));
else node.appendChild(mDOM.createTextNode(value));
}
else if (nodeChildren.getLength() == 1) {
Node child = nodeChildren.item(0);
switch (child.getNodeType()) {
case Node.TEXT_NODE:
if (useCdata) {
node.replaceChild(mDOM.createCDATASection(value), child);
break;
}
case Node.CDATA_SECTION_NODE:
child.setNodeValue(value);
break;
default:
throw new InvalidDataException("Node '"+node.getNodeName()+"' can't set child node of type "+child.getNodeType());
}
}
else
throw new InvalidDataException("Node '"+node.getNodeName()+"' shall have 0 or 1 children only #children:"+nodeChildren.getLength());
}
else if (type == Node.ATTRIBUTE_NODE) {
if (useCdata)
throw new InvalidDataException("Node '"+node.getNodeName()+"' can't set cdata of attribute");
node.setNodeValue(value);
}
else {
throw new InvalidDataException("Cannot handle node name:"+node.getNodeName() + " typeCode:"+type);
}
}
/**
* Retrieves the text, CDATA or attribute value of the Node selected by the XPath
*
* @param xpath The path to access the selected Node
* @return the value of the selected Node
* @throws XPathExpressionException xpath was not valid (e.g. there is no such node)
* @throws InvalidDataException xpath result is not text, CDATA or attribute
*/
public String getFieldByXPath(String xpath) throws XPathExpressionException, InvalidDataException {
Node field = getNodeByXPath(xpath);
if (field == null) throw new InvalidDataException("Cannot resolve xpath:"+xpath);
else return getNodeValue(field);
}
/**
* Determines if the NodeList is actually a single field, an element with text data only
*
* @param elements NodeList
* @return true if the NodeList has a single field of type ELEMENT_NODE
*/
private boolean hasSingleField(NodeList elements) {
return (elements != null && elements.getLength() > 0 && elements.item(0).getNodeType() == Node.ELEMENT_NODE);
}
/**
* Sets an Attribute value by name of the given Element. It only updates existing Attributes.
* If data is null, Element exists and the remove flag is true the node is removed.
*
* @param element the Element to search
* @param name the name of the Attribute
* @param data the value to set
* @param remove flag to remove existing node when data is null
* @throws InvalidDataException the attribute was not found
*/
public void setAttribute(Element element, String name, String data, boolean remove) throws InvalidDataException {
if (data == null && remove) {
Logger.msg(7, "Outcome.setAttribute() - removing name:"+name);
if (element.hasAttribute(name)) element.removeAttribute(name);
return;
}
if (element.hasAttribute(name)) element.getAttributeNode(name).setValue(data);
else throw new InvalidDataException("Invalid name:'"+name+"'");
}
/**
* Sets an Attribute value by name of the given Element. It only updates existing Attributes.
*
* @param element the Element to search
* @param name the name of the Attribute
* @param data the value to set
* @throws InvalidDataException the Attribute was not found
*/
public void setAttribute(Element element, String name, String data) throws InvalidDataException {
setAttribute(element, name, data, false);
}
/**
* Sets the value of an attribute in the root Element. It can only update existing attributes.
*
* @param name the name of the Attribute
* @param data the value to be set
* @param remove flag to remove the element if the data is null
* @throws InvalidDataException attribute was not found
*/
public void setAttribute(String name, String data, boolean remove) throws InvalidDataException {
setAttribute(mDOM.getDocumentElement(), name, data, remove);
}
/**
* Sets an Attribute value by name of the root Element.
*
* @param name the name of the Attribute
* @param data the value to set
* @throws InvalidDataException the name was not found
*/
public void setAttribute(String name, String data) throws InvalidDataException {
setAttribute(name, data, false);
}
/**
* Sets the value of an attribute in a given Field, i.e. named Element. It can only update existing attributes.
*
* @param field the named Element in the root Element
* @param name the name of the Attribute
* @param data the value to be set
* @param remove flag to remove the element if the data is null
* @throws InvalidDataException Element or attribute was not found
*/
public void setAttributeOfField(String field, String name, String data, boolean remove) throws InvalidDataException {
NodeList elements = mDOM.getDocumentElement().getElementsByTagName(field);
if (hasSingleField(elements))
setAttribute((Element)elements.item(0), name, data, remove);
else
throw new InvalidDataException("'"+field+"' is invalid or not a single field");
}
/**
* Sets the value of an attribute in a given Field, i.e. named Element. It can only update existing attributes.
*
* @param field the named Element in the root Element
* @param name the name of the Attribute
* @param data the value to be set
* @throws InvalidDataException Element or attribute was not found
*/
public void setAttributeOfField(String field, String name, String data) throws InvalidDataException {
setAttributeOfField(field, name, data, false);
}
/**
* Sets the textNode value of the named Element of the given Element. It only updates existing Element.
*
* @param element Element to use
* @param name the name of the Element
* @param data the data to be set
* @param remove flag to remove the element if the data is null
* @throws InvalidDataException the name was not found or there were more Elements with the given name
*/
public void setField(Element element, String name, String data, boolean remove) throws InvalidDataException {
NodeList elements = element.getElementsByTagName(name);
if (hasSingleField(elements)) {
if (data == null && remove) {
Logger.msg(7, "Outcome.setField() - removing name:"+name);
element.removeChild(elements.item(0));
return;
}
//Setting nodeValue to null could corrupt document
if (data == null) data = "";
setNodeValue(elements.item(0), data);
}
else
throw new InvalidDataException("'"+name+"' is invalid or not a single field");
}
/**
* Sets the textNode value of the named Element of the given Element. It only updates existing Element.
*
* @param element Element to use
* @param name the name of the Element
* @param data the data to be set
* @throws InvalidDataException the name was not found or there were more Elements with the given name
*/
public void setField(Element element, String name, String data) throws InvalidDataException {
setField(element, name, data, false);
}
/**
* Sets the textNode value of the named Element of the root Element. It only updates existing Element.
*
* @param name the name of the Element
* @param data the data to be set
* @param remove flag to remove the element if the data is null
* @throws InvalidDataException the name was not found or there were more Elements with the given name
*/
public void setField(String name, String data, boolean remove) throws InvalidDataException {
setField(mDOM.getDocumentElement(), name, data, remove);
}
/**
* Sets the textNode value of the named Element of the root Element. It only updates existing Element.
*
* @param name the name of the Element
* @param data the data to be set
* @throws InvalidDataException the name was not found or there were more Elements with the given name
*/
public void setField(String name, String data) throws InvalidDataException {
setField(name, data, false);
}
/**
* Sets the text, CDATA or attribute value of the Node selected by the XPath. It only updates existing Nodes.
*
* @param xpath the selected Node to be updated
* @param data string containing the data
* @throws XPathExpressionException xpath is invalid
* @throws InvalidDataException xpath result is not text, CDATA or attribute
*/
public void setFieldByXPath(String xpath, String data) throws XPathExpressionException, InvalidDataException {
setFieldByXPath(xpath, data, false);
}
/**
* Sets the text, CDATA or attribute value of the Node selected by the XPath. It only updates existing Nodes.
* If data is null and the node exists, the node is removed
*
* @param xpath the selected Node to be updated
* @param data string containing the data, it can be null
* @param remove flag to remove existing node when data is null
* @throws XPathExpressionException xpath is invalid
* @throws InvalidDataException xpath result is not text, CDATA or attribute
*/
public void setFieldByXPath(String xpath, String data, boolean remove) throws XPathExpressionException, InvalidDataException {
if (StringUtils.isBlank(xpath)) throw new InvalidDataException("Xpath is null or empty string");
if (data == null && remove) {
Logger.msg(7, "Outcome.setFieldByXPath() - removing field xpath");
removeNodeByXPath(xpath);
return;
}
//Setting nodeValue to null could corrupt document
if (data == null) data = "";
Node field = getNodeByXPath(xpath);
if (field == null) {
Logger.error(getData());
throw new InvalidDataException("Xpath '"+xpath+"' is invalid");
}
else
setNodeValue(field, data);
}
/**
* Append the new Node created from xmlFragment as a child of the Node selected by the XPath
*
* @param xpath the selected parent node
* @param xmlFragment string containing the xml fragment
* @return the Node just added
*/
public Node appendXmlFragment(String xpath, String xmlFragment) throws InvalidDataException {
try {
Node parentNode = getNodeByXPath(xpath);
Node newNode = parse(xmlFragment).getDocumentElement();
return parentNode.appendChild(mDOM.importNode(newNode, true));
}
catch (SAXException | IOException | XPathExpressionException e) {
Logger.error(e);
throw new InvalidDataException(e.getMessage());
}
}
/**
* Returns the serialised DOM as a string
*
* @return the xml string
*/
public String getData() {
try {
return serialize(mDOM, false);
}
catch (InvalidDataException e) {
Logger.error(e);
return null;
}
}
@Deprecated
public String getSchemaType() {
if (mSchema == null) throw new IllegalArgumentException("Outcome must have valid Schema");
return mSchema.getName();
}
@Deprecated
public int getSchemaVersion() {
if (mSchema == null) throw new IllegalArgumentException("Outcome must have valid Schema");
return mSchema.getVersion();
}
/**
* Returns {@link ClusterType#OUTCOME}
*/
@Override
public ClusterType getClusterType() {
return OUTCOME;
}
@Override
public String getClusterPath() {
if (mID == null || mID == NONE || mSchema == null) throw new IllegalArgumentException("Outcome must have valid ID and Schema");
return getClusterType()+"/"+mSchema.getName()+"/"+mSchema.getVersion()+"/"+mID;
}
public static Document newDocument() throws SAXException, IOException {
return parse((InputSource)null);
}
/**
* Parses the xml string into a DOM tree
*
* @param xml string to be parsed, can be null. When xml is null it creates empty Document.
* @return the parsed Document
*
* @throws SAXException error parsing document
* @throws IOException any IO errors occur
*/
public static Document parse(String xml) throws SAXException, IOException {
return parse(new InputSource(new StringReader(xml)));
}
/**
* Parses the input source into a DOM tree. When input source is null it creates empty Document.
*
* @param xml string to be parsed, can be null.
* @return the parsed Document
*
* @throws SAXException error parsing document
* @throws IOException any IO errors occur
*/
public static Document parse(InputSource xml) throws SAXException, IOException {
synchronized (parser) {
if (xml!=null) return parser.parse(xml);
else return parser.newDocument();
}
}
/**
* Retrieves an Attribute value by name of the given Element.
*
* @param element the Element to query
* @param name The name of the attribute to retrieve.
* @return The value as a string, or null if that attribute does not have a specified or default value.
*/
public String getAttribute(Element element, String name) {
String value = element.getAttribute(name);
if (StringUtils.isNotBlank(value)) return value;
else return null;
}
/**
* Retrieves an Attribute value by name of the root Element.
*
* @param name The name of the attribute to retrieve.
* @return The value as a string, or null if that attribute does not have a specified or default value.
*/
public String getAttribute(String name) {
return getAttribute(mDOM.getDocumentElement(), name);
}
/**
* Retrieves an Attribute value by name from the named Element.
*
* @param field The name of the field.
* @param attribute The name of the attribute to retrieve.
* @return The value as a string, or null if that attribute does not have a specified or default value.
*/
public String getAttributeOfField(String field, String attribute) {
NodeList elements = mDOM.getDocumentElement().getElementsByTagName(field);
if (hasSingleField(elements)) {
String value = ((Element)elements.item(0)).getAttribute(attribute);
if (StringUtils.isNotBlank(value)) return value;
else return null;
}
else {
Logger.warning("Outcome.getAttributeOfField() - '%s' is invalid or not a single field", field);
return null;
}
}
/**
* Retrieves the textNode value of the named Element of the given Element.
*
* @param element the Element to query
* @param name The name of the Element
* @return The value as a string, or null if that field does not exists
*/
public String getField(Element element, String name) {
try {
NodeList elements = element.getElementsByTagName(name);
if (hasSingleField(elements)) {
if (elements.getLength() > 1)
Logger.warning("Outcome.getField() - '%s' was found multiple times, returning first occurance", name);
return getNodeValue(elements.item(0));
}
else{
Logger.warning("Outcome.getField() - '%s' is invalid or not a single field", name);
}
}
catch (InvalidDataException e) {
Logger.warning("Outcome.getField() - exception:"+e.getMessage());
}
return null;
}
/**
* Retrieves the textNode value of the named Element of the root Element.
*
* @param name The name of the Element
* @return The value as a string, or null if that field does not exists
*/
public String getField(String name) {
return getField( mDOM.getDocumentElement(), name);
}
/**
* Gets a NodeList selected by the xpath
*
* @param xpathExpr the xpath to select the list of Nodes
* @return NodeList
* @throws XPathExpressionException invalid xpath
*/
public NodeList getNodesByXPath(String xpathExpr) throws XPathExpressionException {
return (NodeList) evaluateXpath(xpathExpr, XPathConstants.NODESET);
}
/**
* Gets a List selected by the xpath
*
* @param xpathExpr the xpath to select the list of Nodes
* @return Node
* @throws XPathExpressionException invalid xpath
*/
public Node getNodeByXPath(String xpathExpr) throws XPathExpressionException {
return (Node) evaluateXpath(xpathExpr, XPathConstants.NODE);
}
/**
* Removes the node selected by the xpath
*
* @param xpathExpr xpath to select the Node
* @return the Node removed
* @throws XPathExpressionException invalid xpath
* @throws InvalidDataException invalid xpath
*/
public Node removeNodeByXPath(String xpathExpr) throws XPathExpressionException, InvalidDataException {
if (StringUtils.isBlank(xpathExpr)) throw new InvalidDataException("Xpath is null or empty string");
Node nodeToTemove = getNodeByXPath(xpathExpr);
if (nodeToTemove == null) {
Logger.error("Xpath '"+xpathExpr+"' is invalid\n" + getData());
throw new InvalidDataException("Xpath '"+xpathExpr+"' is invalid");
}
return nodeToTemove.getParentNode().removeChild(nodeToTemove);
}
/**
* Serialize the Given Document
*
* @param doc document to be serialized
* @param prettyPrint if the xml is pretty printed or not
* @return the xml string
* @throws InvalidDataException Transformer Exception
*/
static public String serialize(Node node, boolean prettyPrint) throws InvalidDataException {
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer;
try {
transformer = tf.newTransformer();
}
catch (TransformerConfigurationException ex) {
Logger.error(ex);
return "";
}
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.INDENT, prettyPrint?"yes":"no");
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
Writer out = new StringWriter();
try {
transformer.transform(new DOMSource(node), new StreamResult(out));
}
catch (Exception e) {
Logger.error(e);
throw new InvalidDataException(e.getMessage());
}
return out.toString();
}
/**
* Reads the all Attributes and child Elements of the given Node
*
* @param node the node to work with
* @return a Map as a key/value pairs of Attribute/Element names with their value
*/
public Map getRecordOfNode(Node node) {
HashMap record = new HashMap<>();
NodeList elements = node.getChildNodes();
for (int i = 0; i < elements.getLength(); i++) {
if (elements.item(i).getNodeType() == Node.ELEMENT_NODE) {
String name = elements.item(i).getNodeName();
String value = elements.item(i).getTextContent();
record.put(name, value);
}
}
NamedNodeMap attrs = node.getAttributes();
for (int i = 0; i < attrs.getLength(); i++) {
String name = attrs.item(i).getNodeName();
String value = attrs.item(i).getTextContent();
record.put(name, value);
}
return record;
}
/**
* Reads the all Attributes and child Elements of the root Element
*
* @return a Map as a key/value pairs of Attribute/Element names with their value
*/
public Map getRecord() {
return getRecordOfNode( mDOM.getDocumentElement() );
}
/**
* Reads the all Attributes and child Elements of the Node selected by the xpath
*
* @param xpath the xpath pointing to the Node
* @return a Map as a key/value pairs of Attribute/Element names with their value
* @throws XPathExpressionException xpath is invalid
*/
public Map getRecord(String xpath) throws XPathExpressionException {
return getRecordOfNode( getNodeByXPath(xpath) );
}
/**
* Reads all Attributes and child Elements of the list of Node selected by the xpath
*
* @param xpath selecting the list of Nodes
* @return List of Maps as a key/value pairs of Attribute/Element names with their value
* @throws XPathExpressionException xpath is invalid
*/
public List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy