org.yawlfoundation.yawl.scheduling.util.XMLUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.yawlfoundation.yawl.scheduling.util.XMLUtils.java

Source

/*
 * Copyright (c) 2004-2012 The YAWL Foundation. All rights reserved.
 * The YAWL Foundation is a collaboration of individuals and
 * organisations who are committed to improving workflow technology.
 *
 * This file is part of YAWL. YAWL 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.
 *
 * YAWL 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 YAWL. If not, see <http://www.gnu.org/licenses/>.
 */

package org.yawlfoundation.yawl.scheduling.util;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.custommonkey.xmlunit.Diff;
import org.jdom2.Attribute;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.xpath.XPath;
import org.json.JSONArray;
import org.json.JSONObject;
import org.w3c.dom.ls.LSInput;
import org.w3c.dom.ls.LSResourceResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.yawlfoundation.yawl.scheduling.Constants;
import org.yawlfoundation.yawl.util.JDOMUtil;

import javax.xml.XMLConstants;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.Duration;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import java.io.*;
import java.util.*;

/**
 * 
 * @author tbe
 * @version $Id: java 18313 2010-04-30 10:14:09Z tbe $
 */
public class XMLUtils implements Constants {
    private static Logger logger = LogManager.getLogger(XMLUtils.class);

    private static String schemaFilePathName = "/rup.xsd";
    private static Map<String, Document> schemaDocs = new HashMap<String, Document>();
    static {
        schemaDocs.put(schemaFilePathName, null);
        schemaDocs.put("/common.xsd", null);
    }

    private static Validator validator = null;

    private static Validator getValidator() throws IOException, SAXException {
        if (validator == null) {
            SchemaFactory xmlSchemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
            File schemafile = new File(schemaFilePathName);
            int idx = schemaFilePathName.lastIndexOf(schemafile.getName());
            String path = schemaFilePathName.substring(0, idx);
            xmlSchemaFactory.setResourceResolver(new XMLUtils().new ResourceResolver(path));
            Reader reader = new StringReader(convertSchemaToString(schemaFilePathName));
            Schema schema = xmlSchemaFactory.newSchema(new StreamSource(reader));
            validator = schema.newValidator();
        }
        return validator;
    }

    public static Collection<Document> getSchemaDocs() {
        for (String pathName : schemaDocs.keySet()) {
            if (schemaDocs.get(pathName) == null) {
                try {
                    schemaDocs.put(pathName, JDOMUtil.stringToDocument(convertSchemaToString(pathName)));
                } catch (IOException e) {
                    logger.error("cannot parse " + pathName, e);
                }
            }
        }
        return schemaDocs.values();
    }

    /**
     * return constants defined in XSD enumerations sind als simpleType definiert
     * 
     * @return
     */
    public static List<String> getEnumerationFromSchema(String name) {
        List<String> result = new ArrayList<String>();
        try {
            String xpath = "//xs:simpleType[@name='" + name + "']/xs:restriction/xs:enumeration";

            for (Document doc : getSchemaDocs()) {
                List<Element> elements = getXMLObjects(doc, xpath);
                for (Element element : elements) {
                    result.add(element.getAttributeValue("value"));
                }
                if (!result.isEmpty()) {
                    break;
                }
            }
        } catch (Exception e) {
            logger.error("cannot load constants '" + name + "' from XSD", e);
        }
        return result;
    }

    public static String getDefaultFromSchema(String elementName) {
        String attr = getAttributeFromSchema(elementName, "xs:annotation/@perikles:default");
        return attr == null ? "" : attr;
    }

    public static String getUnitFromSchema(String elementName) {
        String attr = getAttributeFromSchema(elementName, "xs:annotation/@perikles:unit");
        return attr == null ? "" : attr;
    }

    public static boolean isVisibleFromSchema(String elementName) {
        String attr = getAttributeFromSchema(elementName, "xs:annotation/@perikles:visible");
        return attr == null || !attr.equalsIgnoreCase("hidden");
    }

    public static boolean isReadonlyFromSchema(String elementName) {
        String attr = getAttributeFromSchema(elementName, "xs:annotation/@perikles:readonly");
        return attr != null && attr.equalsIgnoreCase("true");
    }

    public static boolean isEnumerationFromSchema(String elementName) {
        String attr = getAttributeFromSchema(elementName, "xs:annotation/@perikles:source");
        return attr != null && attr.equalsIgnoreCase("enumeration");
    }

    public static boolean isRequiredFromSchema(String elementName) {
        String attr = getAttributeFromSchema(elementName, "@minOccurs");
        return attr == null || !attr.equals("0");
    }

    private static String getAttributeFromSchema(String elementName, String attributeXPath) {
        String value = null;
        try {
            String xpath = "//xs:element[@name='" + elementName + "']/" + attributeXPath;

            for (Document doc : getSchemaDocs()) {
                Attribute attr = getAttribute(doc, xpath);
                if (attr != null) {
                    value = attr.getValue();
                    break;
                }
            }
        } catch (Exception e) {
            logger.error("cannot load '" + attributeXPath + "' from element '" + elementName + "' from XSD", e);
        }
        return value;
    }

    @SuppressWarnings("unchecked")
    public static void setDefaults(Element elem) {
        List<Element> childs = (List<Element>) elem.getChildren();
        if (childs.isEmpty()) {
            elem.setText(getDefaultFromSchema(elem.getName()));
        } else {
            for (Element child : (List<Element>) elem.getChildren()) {
                setDefaults(child);
            }
        }
    }

    /**
     * Internal method that helps to read an XSD file from the file system;
     * location: where the Java classes are located
     * 
     * @param schemaFileName - name of the XML Schema file
     * @return String representation of the XML Schema data model
     * @throws IOException
     */
    private static String convertSchemaToString(String schemaFileName) throws IOException {
        InputStream inputStream = null;
        try {
            inputStream = Utils.class.getResourceAsStream(schemaFileName); // read only below the classpath

            /*
             * To convert the InputStream to String we use the
             * BufferedReader.readLine() method. We iterate until the
             * BufferedReader returns null which means there's no more data to read.
             * Each line will be appended to a StringBuilder and returned as String.
             */
            StringBuilder stringBuilder = new StringBuilder();
            String line;
            InputStreamReader streamReader = new InputStreamReader(inputStream, "UTF-8");
            BufferedReader bufferedReader = new BufferedReader(streamReader);
            while ((line = bufferedReader.readLine()) != null) {
                stringBuilder.append(line).append("\n");
            }

            return stringBuilder.toString();

            // strip off any junk in the prolog before feeding the XML into a
            // parser
            // @see
            // http://mark.koli.ch/2009/02/resolving-orgxmlsaxsaxparseexception-content-is-not-allowed-in-prolog.html
            // return sb.toString().trim().replaceFirst("^([\\W]+)<","<");
        } finally {
            try {
                if (inputStream != null)
                    inputStream.close();
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Datentypberprfung und angabe des betroffenen elements, es werden alle
     * gefundenen Fehler ausgegeben Xerces DocumentBuilder method, called by
     * validator.validate(), isn't thread safe
     * 
     * @param msgText
     * @return
     * @throws IOException
     * @throws SAXException
     */
    public static synchronized void validate(String msgText) throws IOException, SAXException {
        Validator validator = getValidator();
        SimpleErrorHandler handler = new XMLUtils().new SimpleErrorHandler();
        validator.setErrorHandler(handler);
        validator.validate(new StreamSource(new StringReader(msgText)));

        if (handler.hasErrors()) {
            throw new SAXException(Utils.toString(handler.getErrorMsgs()));
        }
    }

    public static void validate(Element msg) throws IOException, SAXException {
        validate(Utils.element2String(msg, false));
    }

    /**
     * validates type of resource utilisation plan against schema element name
     * TODO@tbe: name of schema element must be unique!
     * Activity.SurgicalProcedure.Reservation.#1.Resource.Type
     */
    private static String validateType(Element element, String schemaName, List<Element> restrictions,
            boolean withValidation) throws DatatypeConfigurationException, IOException, JDOMException {
        String xpath = "//xs:simpleType[@name='" + schemaName + "']";

        Element typeElement = null;
        for (Document doc : getSchemaDocs()) {
            typeElement = getElement(doc, xpath);
            if (typeElement != null) {
                break;
            }
        }

        if (typeElement == null) {
            xpath = "//xs:complexType[@name='" + schemaName + "']";
            for (Document doc : getSchemaDocs()) {
                typeElement = getElement(doc, xpath);
                if (typeElement != null) {
                    break;
                }
            }
        }

        if (typeElement == null) {
            logger.error("cannot find type in schema, with xpath: " + xpath);
            return null;
        } else {
            return validateElement(element, typeElement, restrictions, withValidation);
        }
    }

    /**
     * validates element of resource utilisation plan against type element from
     * schema return cs class for element
     */
    public static String validateElement(Element element, Element typeElement, List<Element> restrictions,
            boolean withValidation) throws DatatypeConfigurationException, IOException, JDOMException {
        String cssClass = null;

        String type = typeElement.getAttributeValue("type");
        if (type == null) {
            type = typeElement.getAttributeValue("base");
            if (typeElement.getName().equals("restriction")) {
                restrictions.addAll(typeElement.getChildren());
                // logger.debug("found restrictions: "+Utils.toString(restrictions));
            }
        }

        if (type == null) {
            for (Element schemaVarChild : (List<Element>) typeElement.getChildren()) {
                if (!schemaVarChild.getName().equals("element")) {
                    cssClass = validateElement(element, schemaVarChild, restrictions, withValidation);
                }
            }
            return cssClass;
        }

        Object o = null;
        if (isDatatypeXSD(type, XSDDatatypes_Int)) {
            o = getIntegerValue(element, withValidation);
            cssClass = CSS_INTINPUT;
        } else if (isDatatypeXSD(type, XSDDatatypes_Long)) {
            o = getLongValue(element, withValidation);
            cssClass = CSS_INTINPUT;
        } else if (isDatatypeXSD(type, XSDDatatypes_Double)) {
            o = getDoubleValue(element, withValidation);
            cssClass = CSS_INTINPUT;
        } else if (isDatatypeXSD(type, XSDDatatypes_DateTime)) {
            o = getDateValue(element, withValidation);
            cssClass = CSS_DATEINPUT;
        } else if (isDatatypeXSD(type, XSDDatatypes_Duration)) {
            o = getDurationValueInMinutes(element, withValidation);
            cssClass = CSS_DURATIONINPUT;
        } else if (isDatatypeXSD(type, XSDDatatypes_Boolean)) {
            o = getBooleanValue(element, withValidation);
            cssClass = CSS_BOOLEANINPUT;
        } else if (isDatatypeXSD(type, XSDDatatypes_String)) {
            o = getStringValue(element, withValidation);
            cssClass = CSS_TEXTINPUT;
        } else {
            cssClass = validateType(element, type, restrictions, withValidation);
        }

        validateRestrictions(element, type, o, restrictions, withValidation);

        return cssClass;
    }

    private static boolean isDatatypeXSD(String type, String[] xsdDatatypes) {
        for (String xsdType : xsdDatatypes) {
            if (type.equals("xs:" + xsdType)) {
                return true;
            }
        }
        return false;
    }

    /**
     * check restrictions given in XSD
     * 
     * @param o
     * @param restrictions
     */
    private static void validateRestrictions(Element element, String type, Object o, List<Element> restrictions,
            boolean withValidation) throws DatatypeConfigurationException {
        // logger.debug(element.getName() + ": " + o +
        // ", "+Utils.toString(restrictions));
        if (o == null) {
            return;
        }

        // extra handling for enumeration
        Boolean enumerationError = null;

        for (Element restriction : restrictions) {
            String value = restriction.getAttributeValue("value");
            if (restriction.getName().equals("enumeration")) {
                if (enumerationError == null) {
                    enumerationError = true;
                }
                if (enumerationError && value.equals(o.toString())) {
                    enumerationError = false;
                }

            } else if (restriction.getName().equals("length")) {
                if (Integer.valueOf(value) != o.toString().length()) {
                    addErrorValue(element, withValidation, "msgLengthInvalid", new String[] { value });
                }

            } else if (restriction.getName().equals("maxLength")) {
                if (Integer.valueOf(value) < o.toString().length()) {
                    addErrorValue(element, withValidation, "msgLengthLowerEquals", new String[] { value });
                }

            } else if (restriction.getName().equals("minLength")) {
                if (Integer.valueOf(value) > o.toString().length()) {
                    addErrorValue(element, withValidation, "msgLengthGreaterEquals", new String[] { value });
                }

            } else if (restriction.getName().equals("maxExclusive")) {
                String vStr = getIntegerInterpretation(value, type);
                if (Integer.valueOf(vStr) <= Integer.valueOf(o.toString())) {
                    addErrorValue(element, withValidation, "msgLower", new String[] { vStr });
                }

            } else if (restriction.getName().equals("minExclusive")) {
                String vStr = getIntegerInterpretation(value, type);
                if (Integer.valueOf(vStr) >= Integer.valueOf(o.toString())) {
                    addErrorValue(element, withValidation, "msgGreater", new String[] { vStr });
                }

            } else if (restriction.getName().equals("maxInclusive")) {
                String vStr = getIntegerInterpretation(value, type);
                if (Integer.valueOf(vStr) < Integer.valueOf(o.toString())) {
                    addErrorValue(element, withValidation, "msgLowerEquals", new String[] { vStr });
                }

            } else if (restriction.getName().equals("minInclusive")) {
                String vStr = getIntegerInterpretation(value, type);
                if (Integer.valueOf(vStr) > Integer.valueOf(o.toString())) {
                    addErrorValue(element, withValidation, "msgGreaterEquals", new String[] { vStr });
                }

            } else if (restriction.getName().equals("whiteSpace")) {
                // nothing to do

            } else {
                // TODO@tbe pattern, fractionDigits, totalDigits
                logger.error("restriction '" + restriction.getName() + "' is not yet implemented");
            }
        }

        if (enumerationError != null && enumerationError) {
            addErrorValue(element, withValidation, "msgUnknownValue");
        }
    }

    /**
     * interpretes value as Integer, depending of type
     * 
     * @param value
     * @param type
     * @return
     */
    private static String getIntegerInterpretation(String value, String type) {
        // logger.debug("value: " + value + ", type: " + type);
        if (isDatatypeXSD(type, XSDDatatypes_Duration)) {
            return Utils.stringXMLDuration2stringMinutes(value);
        } else {
            return value;
        }
    }

    class SimpleErrorHandler implements ErrorHandler {
        private ArrayList<String> errors = new ArrayList<String>();

        public void warning(SAXParseException e) throws SAXException {
            // logger.debug("WARN : " + e.getMessage());
        }

        public void error(SAXParseException e) throws SAXException {
            // logger.debug("ERROR: " + e.getMessage());
            errors.add(e.getMessage());
        }

        public void fatalError(SAXParseException e) throws SAXException {
            // logger.debug("FATAL: " + e.getMessage());
            errors.add(e.getMessage());
        }

        public boolean hasErrors() {
            return !errors.isEmpty();
        }

        /**
         * returns complete xsd error message, e.g.
         * "cvc-datatype-valid.1.2.1: 'x' is not a valid value for 'integer'."
         * 
         * @return
         */
        public List<String> getErrorsFull() {
            return errors;
        }

        /**
         * returns only message of xsd error, e.g.
         * "'x' is not a valid value for 'integer'."
         * 
         * @return
         */
        public List<String> getErrorMsgs() {
            ArrayList<String> errorMsgs = new ArrayList<String>();
            for (String msg : errors) {
                errorMsgs.add(msg.substring(msg.indexOf(":") + 2));
            }
            return errorMsgs;
        }

        /**
         * returns messages, which are not datatype errors private method, because
         * its experimental. doesn't work because there are any further xsd error
         * prefixes
         * 
         * @return
         */
        private List<String> getErrorMsgsWithoutDatatypeCheck() {
            ArrayList<String> errorMsgs = new ArrayList<String>();
            for (String msg : errors) {
                if (!msg.startsWith("cvc-datatype-valid") && !msg.startsWith("cvc-maxInclusive-valid")) {
                    errorMsgs.add(msg.substring(msg.indexOf(":") + 2));
                }
            }
            return errorMsgs;
        }

    }

    public static String getErrorValue(Element e) {
        return getAttributeValue(e, XML_ERROR);
    }

    public static String getWarningValue(Element e) {
        return getAttributeValue(e, XML_WARNING);
    }

    private static String getAttributeValue(Element e, String attr) {
        return e.getAttribute(attr) == null ? null : e.getAttribute(attr).getValue();
    }

    public static void addErrorValue(Element e, boolean withValidation, String value, String... args) {
        // logger.debug(e.getName()+"='"+e.getText()+"', isRequiredFromSchema(e.getName())="
        // + isRequiredFromSchema(e.getName())+", withValidation=" +
        // withValidation+", value=" + value);
        if (!e.getText().isEmpty()) {
            addAttributeValue(e, XML_ERROR, value, args); // malformed input ->
                                                          // error
        } else if (isRequiredFromSchema(e.getName())) {
            if (withValidation) {
                addAttributeValue(e, XML_ERROR, value, args); // missing input ->
                                                              // error
            } else {
                // addWarningValue(e, value, args); // missing input, but don't
                // validate
            }
        }
    }

    public static void addWarningValue(Element e, String value, String... args) {
        addAttributeValue(e, XML_WARNING, value, args);
    }

    public static void addAttributeValue(Element e, String attr, String value, String... args) {
        // logger.debug("e="+e+", attr=" + attr+", value=" + value+", args=" +
        // args);
        try {
            String json = e.getAttribute(attr) == null ? "[]" : e.getAttribute(attr).getValue();
            if (!json.startsWith("["))
                json = String.format("[%s]", json);
            JSONArray jsonArr = new JSONArray(json);

            // value already exist?
            for (int i = 0; i < jsonArr.length(); i++) {
                JSONObject jsonObj = jsonArr.getJSONObject(i);
                if (jsonObj.has(value)) {
                    // logger.debug("AttributeValue already exist: " + value);
                    return;
                }
            }

            Map valueAndArgs = new TreeMap();
            valueAndArgs.put(value, args);

            JSONObject jsonObj = new JSONObject(valueAndArgs);
            jsonArr.put(jsonObj);

            e.setAttribute(attr, jsonArr.toString());
        } catch (Exception e1) {
            logger.error("cannot add Attribute", e1);
        }
    }

    public static void removeAttributes(Element element, String attr) {
        removeAttribute(element, attr);
        for (Element child : (List<Element>) element.getChildren()) {
            removeAttribute(child, attr);
        }
    }

    public static void removeAttribute(Element e, String attr) {
        try {
            e.removeAttribute(attr);
        } catch (Exception e1) {
            logger.error("cannot remove Attribute", e1);
        }
    }

    public static boolean hasErrors(Element element) {
        return hasAttributeValues(element, XML_ERROR);
    }

    public static boolean hasWarnings(Element element) {
        return hasAttributeValues(element, XML_WARNING);
    }

    private static boolean hasAttributeValues(Element element, String attr) {
        String value = getAttributeValue(element, attr);
        // logger.debug("element: "+element.getName() + "." + attr+" = '" + value
        // + "'");
        if (value != null) {
            return true;
        } else {
            for (Element child : (List<Element>) element.getChildren()) {
                if (hasAttributeValues(child, attr)) {
                    return true;
                }
            }
        }
        return false;
    }

    public static Set<String> getErrors(Element element) {
        return getAttributeValues(element, XML_ERROR);
    }

    private static Set<String> getAttributeValues(Element element, String attr) {
        Set<String> values = new HashSet<String>();
        String value = getAttributeValue(element, attr);
        // logger.debug("element: "+element.getName() + "." + attr+" = '" + value
        // + "'");
        if (value != null) {
            values.add(value);
        }

        for (Element child : (List<Element>) element.getChildren()) {
            values.addAll(getAttributeValues(child, attr));
        }

        return values;
    }

    public static Date getDateValue(Element element, boolean withValidation) {
        try {
            return Utils.string2Date(element.getText(), Utils.DATETIME_PATTERN_XML);
        } catch (Exception e) {
            // logger.error("'" + element.getName() + "' must be datetime, " +
            // e.getMessage());
            if (element == null) {
                logger.debug("#####  element is NULL  #####");
            }

            addErrorValue(element, withValidation, "msgDateError");
            return null;
        }
    }

    public static void setDateValue(Element element, Date value) {
        try {
            setStringValue(element, Utils.date2String(value, Utils.DATETIME_PATTERN_XML));
        } catch (Exception e) {
            logger.error("'" + element.getName() + "' must be datetime, " + e.getMessage());
        }
    }

    public static void setDurationValue(Element element, long value) {
        try {
            DatatypeFactory df = DatatypeFactory.newInstance();
            Duration dur = df.newDuration(value);
            setStringValue(element, String.valueOf(dur.toString()));
        } catch (Exception e) {
            logger.error("'" + element.getName() + "' must be Duration, " + e.getMessage());
        }
    }

    public static void setLongValue(Element element, Long value) {
        try {
            setStringValue(element, String.valueOf(value));
        } catch (Exception e) {
            logger.error("'" + element.getName() + "' must be Long, " + e.getMessage());
        }
    }

    public static String getStringValue(Element element, boolean withValidation) {
        if (element.getText().isEmpty()) {
            addErrorValue(element, withValidation, "msgStringError");
        }
        return element.getText();
    }

    public static void setStringValue(Element element, String value) {
        element.setText(value);
        XMLUtils.removeAttribute(element, XML_WARNING);
        XMLUtils.removeAttribute(element, XML_ERROR);
    }

    public static Duration getDurationValue(Element element, boolean withValidation) {
        try {
            // logger.debug("element.getText():" + element.getText());
            Duration d = Utils.stringXML2Duration(element.getText());
            return d;
        } catch (DatatypeConfigurationException e) {
            logger.error("wrong DatatypeConfiguration", e);
            addErrorValue(element, withValidation, "msgTechnicalError");
            return null;
        } catch (Exception e) {
            // logger.error("'" + element.getName() + "' must be duration", e);
            addErrorValue(element, withValidation, "msgDurationError");
            return null;
        }
    }

    public static Integer getDurationValueInMinutes(Element element, boolean withValidation) {
        try {
            Duration d = getDurationValue(element, withValidation);
            return Utils.duration2Minutes(d);
        } catch (Exception e) {
            // logger.error("'" + element.getName() + "' must be duration", e);
            addErrorValue(element, withValidation, "msgIntegerError");
            return null;
        }
    }

    public static Double getDoubleValue(Element element, boolean withValidation) {
        try {
            return Double.parseDouble(element.getText());
        } catch (Exception e) {
            // logger.error("'" + element.getName() + "' must be double, " +
            // e.getMessage());
            addErrorValue(element, withValidation, "msgDoubleError");
            return null;
        }
    }

    public static Integer getIntegerValue(Element element, boolean withValidation) {
        try {
            return Integer.parseInt(element.getText());
        } catch (Exception e) {
            // logger.error("'" + element.getName() + "' must be integer, " +
            // e.getMessage());
            addErrorValue(element, withValidation, "msgIntegerError");
            // logger.error("-----------element '" + element.getName() + "' = " +
            // Utils.object2String(element));
            return null;
        }
    }

    public static Long getLongValue(Element element, boolean withValidation) {
        try {
            return Long.parseLong(element.getText());
        } catch (Exception e) {
            // logger.error("'" + element.getName() + "' must be long, " +
            // e.getMessage());
            addErrorValue(element, withValidation, "msgIntegerError");
            // logger.error("-----------element '" + element.getName() + "' = " +
            // Utils.object2String(element));
            return null;
        }
    }

    public static Boolean getBooleanValue(Element element, boolean withValidation) {
        try {
            String b = element.getText();
            if (b.equals("0") || b.equalsIgnoreCase("false")) {
                return Boolean.FALSE;
            }
            if (b.equals("1") || b.equalsIgnoreCase("true")) {
                return Boolean.TRUE;
            }
            throw new Exception();
        } catch (Exception e) {
            addErrorValue(element, withValidation, "msgBooleanError");
            return null;
        }
    }

    public static String getXPATH_RUP() {
        return "/" + XML_RUP;
    }

    public static String getXPATH_Activities(String... activityNames) {
        String xpath = "";
        if (activityNames != null) {
            for (String activityName : activityNames) {
                if (activityName == null) {
                    continue;
                }

                if (!xpath.isEmpty()) {
                    xpath += " or ";
                }
                xpath += XML_ACTIVITYNAME + "='" + activityName + "'";
            }
        }
        return getXPATH_RUP() + "/" + XML_ACTIVITY + (xpath.isEmpty() ? "" : "[" + xpath + "]");
    }

    public static String getXPATH_ActivitiesElement(String[] activityNames, String elementName, Integer index) {
        return getXPATH_Activities(activityNames) + "/" + elementName + (index == null ? "" : "[" + index + "]");
    }

    public static String getXPATH_ActivityElement(String activityName, String elementName, Integer index) {
        return getXPATH_Activities(activityName) + "/" + elementName + (index == null ? "" : "[" + index + "]");
    }

    public static String getXPATH_ResOrUtilElement(String activityName, String resOrUtilName, Integer resOrUtilIdx,
            String elementName) {
        return getXPATH_ActivityElement(activityName, resOrUtilName, resOrUtilIdx) + "/" + elementName;
    }

    public static String getXPATH_Resource(String activityName, Integer resIdx) {
        return getXPATH_ActivityElement(activityName, XML_RESERVATION, resIdx) + "/" + XML_RESOURCE;
    }

    public static String getXPATH_ResourceElement(String activityName, Integer resIdx, String elementName) {
        return getXPATH_Resource(activityName, resIdx) + "/" + elementName;
    }

    public static String getXPATH_ResourceElement(String activityName, Integer resIdx, String elementName,
            String elementText) {
        return getXPATH_ResourceElement(activityName, null, elementName) + "[text()='" + elementText + "']";
    }

    public static boolean different(Element e1, Element e2) throws IOException, SAXException {
        // logger.debug("e1="+Utils.element2String(e1, false));
        // logger.debug("e2="+Utils.element2String(e2, false));
        Diff diff = new Diff(Utils.element2String(e1, false), Utils.element2String(e2, false));
        boolean similiar = diff.similar();
        // logger.debug("identical="+diff.identical()+", similar="+similiar);
        return !similiar;
    }

    /**
     * compares textual content of two elements which are get with
     * element.getChildText() content of a non-existing element is equal to
     * content of an element without text
     * 
     * @param t1
     * @param t2
     * @return
     */
    public static boolean isEqualXMLText(String t1, String t2) {
        if (t1 == null && t2 == null) {
            return true;
        } else if (t1 == null) {
            return t2.isEmpty();
        } else if (t2 == null) {
            return t1.isEmpty();
        } else {
            return t1.equals(t2);
        }
    }

    @SuppressWarnings("unchecked")
    public static List getXMLObjects(Document doc, String xpath) {
        List objects = new ArrayList();
        try {
            XPath xp = XPath.newInstance(xpath);
            objects = xp.selectNodes(doc);
        } catch (Exception e) {
            logger.error("cannot process xpath: " + xpath + " on document: "
                    + Utils.element2String(doc == null ? null : doc.getRootElement(), true));
        }
        return objects;
    }

    @SuppressWarnings("unchecked")
    private static Object getFirstXMLObject(Document doc, String xpath) {
        List objects = getXMLObjects(doc, xpath);
        return objects.isEmpty() ? null : objects.get(0);
    }

    public static List<Element> getElements(Document doc, String xpath) {
        return (List<Element>) getXMLObjects(doc, xpath);
    }

    public static Element getElement(Document doc, String xpath) {
        return (Element) getFirstXMLObject(doc, xpath);
    }

    public static Attribute getAttribute(Document doc, String xpath) {
        return (Attribute) getFirstXMLObject(doc, xpath);
    }

    /**
     * set childText on elem's child, if child doesn't exist, it will be created
     * 
     * @param elem
     * @param childName
      * @param childText
     */
    public static void setChildText(Element elem, String childName, String childText) {
        Element child = elem.getChild(childName);
        if (child == null) {
            child = new Element(childName);
            elem.addContent(child);
        }
        child.setText(childText);
    }

    /**
     * merges content of two elements recursively into element minor following
     * content will be copied: Text, Element, Attribute if conflicts, minor will
     * be overwrite with content of major
     * 
     * @param minor
     * @param major
     */
    public static boolean mergeElements(Element minor, Element major) throws Exception {
        // logger.debug("minor: " + Utils.element2String(minor, false));
        // logger.debug("major: " + Utils.element2String(major, false));
        boolean changed = false;
        if (minor == null) {
            minor = major;
            // logger.debug("set minor = " + Utils.element2String(major, false));
            changed = true;
        } else if (major != null) {
            if (!minor.getText().equals(major.getText())) {
                minor.setText(major.getText());
                // logger.debug("minor.setText("+major.getText()+")");
                changed = true;
            }

            for (Attribute a : (List<Attribute>) major.getAttributes()) {
                Attribute aCopy = (Attribute) Utils.deepCopy(a);
                if (minor.getAttribute(a.getName()) == null
                        || !minor.getAttributeValue(a.getName()).equals(a.getValue())) {
                    minor.setAttribute(aCopy.detach());
                    // logger.debug("minor.setAttribute("+Utils.toString(a)+")");
                    changed = true;
                }
            }

            for (Element e : (List<Element>) major.getChildren()) {
                Element eCopy = (Element) Utils.deepCopy(e);
                List<Element> minorChildren = minor.getChildren(e.getName());
                // logger.debug("minorChildren: " + Utils.toString(minorChildren));
                // logger.debug("e: " + Utils.toString(e));
                Element firstInList = existInList(minorChildren, e);
                if (firstInList == null) {
                    // logger.debug("minor.addContent: " +
                    // Utils.toString(eCopy.detach()));
                    minor = minor.addContent(eCopy.detach());
                    // logger.debug("minor.addContent("+Utils.element2String(e,
                    // false)+")");
                    changed = true;
                } else {
                    changed = mergeElements(firstInList, eCopy) || changed;
                }
            }
        }

        return changed;
    }

    /**
     * returns all activity names from element e recursively
     * 
     * @param e
     * @return
     */
    public static Set<String> getActivityNames(Element e) {
        Set<String> activityNames = new HashSet<String>();
        if (e == null)
            return activityNames;

        if (e.getName().equals(XML_ACTIVITY)) {
            activityNames.add(e.getChildText(XML_ACTIVITYNAME));
        }
        for (Element child : (List<Element>) e.getChildren()) {
            activityNames.addAll(getActivityNames(child));
        }
        return activityNames;
    }

    public static String getCaseId(Document rup) {
        String xpath = getXPATH_RUP() + "/" + XML_CASEID;
        String caseId = getElement(rup, xpath).getText();
        return caseId;
    }

    /**
     * find earliest FROM element of rup
     */
    public static Element getEarliestBeginElement(Document rup, String[] possibleActivities) {
        Element earliestFromElem = null;
        String xpath = getXPATH_ActivitiesElement(possibleActivities, XML_FROM, null);
        List<Element> fromElems = getXMLObjects(rup, xpath);
        for (Element fromElem : fromElems) {
            Date earliestFrom = earliestFromElem == null ? null : getDateValue(earliestFromElem, true);
            Date from = getDateValue(fromElem, true);
            // logger.debug("earlFrom=" + earlFrom + ", from=" + from);
            if (earliestFrom == null || (from != null && earliestFrom.after(from))) {
                earliestFromElem = fromElem;
            }
        }
        //      logger.debug("rup: " + rup.toString());
        //      logger.debug("earlFromElem: " + earliestFromElem.toString());
        return earliestFromElem;
    }

    /**
     * find earliest FROM date of rup
     */
    public static Date getEarliestBeginDate(Document rup) {
        return getDateValue(getEarliestBeginElement(rup, null), true);
    }

    /**
     * find latest TO element of rup
     */
    public static Element getLatestEndElement(Document rup, String[] possibleActivities) {
        Element lateToElem = null;
        String xpath = getXPATH_ActivitiesElement(possibleActivities, XML_TO, null);
        List<Element> toElems = getXMLObjects(rup, xpath);
        for (Element toElem : toElems) {
            Date lateTo = lateToElem == null ? null : getDateValue(lateToElem, true);
            Date to = getDateValue(toElem, true);
            if (lateTo == null || (to != null && lateTo.before(to))) {
                lateToElem = toElem;
            }
        }
        return lateToElem;
    }

    /**
     * find latest TO date of rup
     */
    public static Date getLatestEndDate(Document rup) {
        return getDateValue(getLatestEndElement(rup, null), true);
    }

    /**
     * return first element from list which is equal (in context of perikles) to
     * e
     * 
     * @param list
     * @param e
     * @return
     */
    private static Element existInList(List<Element> list, Element e) {
        for (Element listElem : list) {
            if (elementsEqual(listElem, e)) {
                return listElem;
            }
        }
        return null;
    }

    /**
     * return true if e1 is equal (in context of perikles) to element e2
     * 
     * @param e1
     * @param e2
     * @return
     */
    private static boolean elementsEqual(Element e1, Element e2) {
        String s1 = e1.getName(), s2 = e2.getName();

        if (!s1.equals(s2)) {
            return false;
        } else if (s1.equals(XML_ACTIVITY)) {
            s1 = e1.getChildText(XML_ACTIVITYNAME);
            s2 = e2.getChildText(XML_ACTIVITYNAME);
        } else if (s1.equals(XML_RESERVATION)) {
            try {
                s1 = e1.getChild(XML_RESOURCE).getChildText(XML_ID);
                s2 = e2.getChild(XML_RESOURCE).getChildText(XML_ID);
            } catch (Exception e) {
                s1 = null;
                s2 = null;
            }
        } else if (s1.equals(XML_UTILISATIONREL)) {
            try {
                s1 = e1.getChildText(XML_THISUTILISATIONTYPE) + e1.getChildText(XML_OTHERACTIVITYNAME);
                s1 += e1.getChildText(XML_OTHERUTILISATIONTYPE);
                s2 = e2.getChildText(XML_THISUTILISATIONTYPE) + e2.getChildText(XML_OTHERACTIVITYNAME);
                s2 += e2.getChildText(XML_OTHERUTILISATIONTYPE);
            } catch (Exception e) {
                s1 = null;
                s2 = null;
            }
        }

        if (s1 == null || s2 == null || s1.isEmpty() || s2.isEmpty()) { // nicht entscheidbar
            return false;
        } else {
            return s1.equals(s2);
        }
    }

    /**
     * Workaround for bug in Xerces, @see
     * https://issues.apache.org/jira/browse/XERCESJ-1130 therefore we cannot
     * include other schemas. Original workaround can be found here:
     * 
     * see http
     *      ://stackoverflow.com/questions/1094893/validate-an-xml-file-against
     *      -multiple-schema-definitions
     * see http://pbin.oogly.co.uk/listings/viewlistingdetail/2
     *      a70d763929ce3053085bfaa1d78e2
     * 
     * @author Jon (oogly.co.uk)
     */
    private class ResourceResolver implements LSResourceResolver {
        String _path;

        ResourceResolver(String path) {
            _path = path;
        }

        public LSInput resolveResource(String type, String namespaceURI, String publicId, String systemId,
                String baseURI) {
            InputStream resourceAsStream = null;
            try {
                String filename = _path + systemId;
                resourceAsStream = this.getClass().getResourceAsStream(filename);
            } catch (Exception e) {
                logger.error("cannot load schema: " + systemId, e);
            }
            return new LSInputImpl(publicId, systemId, resourceAsStream);
        }

        protected class LSInputImpl implements LSInput {

            private String _publicId;
            private String _systemId;
            private BufferedInputStream _inputStream;

            public LSInputImpl(String publicId, String sysId, InputStream input) {
                _publicId = publicId;
                _systemId = sysId;
                _inputStream = new BufferedInputStream(input);
            }

            public String getPublicId() {
                return _publicId;
            }

            public void setPublicId(String publicId) {
                _publicId = publicId;
            }

            public String getSystemId() {
                return _systemId;
            }

            public void setSystemId(String systemId) {
                _systemId = systemId;
            }

            public BufferedInputStream getInputStream() {
                return _inputStream;
            }

            public void setInputStream(BufferedInputStream inputStream) {
                _inputStream = inputStream;
            }

            public String getStringData() {
                synchronized (_inputStream) {
                    try {
                        byte[] input = new byte[_inputStream.available()];
                        _inputStream.read(input);
                        return new String(input);
                    } catch (IOException e) {
                        e.printStackTrace();
                        System.out.println("Exception " + e);
                        return null;
                    }
                }
            }

            // required implementations
            public String getBaseURI() {
                return null;
            }

            public InputStream getByteStream() {
                return null;
            }

            public boolean getCertifiedText() {
                return false;
            }

            public Reader getCharacterStream() {
                return null;
            }

            public String getEncoding() {
                return null;
            }

            public void setBaseURI(String baseURI) {
            }

            public void setByteStream(InputStream byteStream) {
            }

            public void setCertifiedText(boolean certifiedText) {
            }

            public void setCharacterStream(Reader characterStream) {
            }

            public void setEncoding(String encoding) {
            }

            public void setStringData(String stringData) {
            }
        }

    }

}