org.mycore.common.xml.MCRXMLFunctions.java Source code

Java tutorial

Introduction

Here is the source code for org.mycore.common.xml.MCRXMLFunctions.java

Source

/*
 *
 * $Revision$ $Date$
 *
 * This file is part of *** M y C o R e *** See http://www.mycore.de/ for
 * details.
 *
 * This program is free software; you can use it, redistribute it and / or
 * modify it under the terms of the GNU General Public License (GPL) as
 * published by the Free Software Foundation; either version 2 of the License or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program, in a file called gpl.txt or license.txt. If not, write to the
 * Free Software Foundation Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307 USA
 */

package org.mycore.common.xml;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.text.MessageFormat;
import java.text.Normalizer;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.ListIterator;
import java.util.Locale;
import java.util.Optional;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.activation.MimetypesFileTypeMap;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;

import org.apache.commons.lang.StringEscapeUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jdom2.JDOMException;
import org.jdom2.output.DOMOutputter;
import org.mycore.common.MCRCache;
import org.mycore.common.MCRCache.ModifiedHandle;
import org.mycore.common.MCRCalendar;
import org.mycore.common.MCRSessionMgr;
import org.mycore.common.MCRSystemUserInformation;
import org.mycore.common.MCRUtils;
import org.mycore.common.config.MCRConfiguration;
import org.mycore.common.config.MCRConfigurationDir;
import org.mycore.datamodel.classifications2.MCRCategLinkReference;
import org.mycore.datamodel.classifications2.MCRCategLinkService;
import org.mycore.datamodel.classifications2.MCRCategLinkServiceFactory;
import org.mycore.datamodel.classifications2.MCRCategory;
import org.mycore.datamodel.classifications2.MCRCategoryDAO;
import org.mycore.datamodel.classifications2.MCRCategoryDAOFactory;
import org.mycore.datamodel.classifications2.MCRCategoryID;
import org.mycore.datamodel.classifications2.MCRLabel;
import org.mycore.datamodel.common.MCRISO8601Date;
import org.mycore.datamodel.common.MCRLinkTableManager;
import org.mycore.datamodel.common.MCRXMLMetadataManager;
import org.mycore.datamodel.metadata.MCRDerivate;
import org.mycore.datamodel.metadata.MCRMetaLinkID;
import org.mycore.datamodel.metadata.MCRMetadataManager;
import org.mycore.datamodel.metadata.MCRObject;
import org.mycore.datamodel.metadata.MCRObjectDerivate;
import org.mycore.datamodel.metadata.MCRObjectID;
import org.mycore.datamodel.metadata.MCRObjectStructure;
import org.mycore.datamodel.niofs.MCRPath;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.ibm.icu.util.Calendar;
import com.ibm.icu.util.GregorianCalendar;

/**
 * @author Thomas Scheffler (yagee)
 * @author Jens Kupferschmidt
 * @author shermann
 * @author Ren\u00E9 Adler (eagle)
 */
public class MCRXMLFunctions {
    static MCRConfiguration CONFIG = MCRConfiguration.instance();

    private static final String HOST_PREFIX = "MCR.remoteaccess_";

    private static final String QUERY_SUFFIX = "_query_servlet";

    private static final String IFS_SUFFIX = "_ifs_servlet";

    private static final String HOST_SUFFIX = "_host";

    private static final String PORT_SUFFIX = "_port";

    private static final String PROTOCOLL_SUFFIX = "_protocol";

    private static final String DEFAULT_PORT = "80";

    private final static String TAG_START = "\\<\\w+((\\s+\\w+(\\s*\\=\\s*(?:\".*?\"|'.*?'|[^'\"\\>\\s]+))?)+\\s*|\\s*)\\>";

    private final static String TAG_END = "\\</\\w+\\>";

    private final static String TAG_SELF_CLOSING = "\\<\\w+((\\s+\\w+(\\s*\\=\\s*(?:\".*?\"|'.*?'|[^'\"\\>\\s]+))?)+\\s*|\\s*)/\\>";

    private final static String HTML_ENTITY = "&[a-zA-Z][a-zA-Z0-9]+;";

    private final static Pattern TAG_PATTERN = Pattern.compile(TAG_START + "((.*?[^\\<]))" + TAG_END,
            Pattern.DOTALL);

    private final static Pattern HTML_MATCH_PATTERN = Pattern.compile(
            "(" + TAG_START + "((.*?[^\\<]))" + TAG_END + ")|(" + TAG_SELF_CLOSING + ")|(" + HTML_ENTITY + ")",
            Pattern.DOTALL);

    private static final Logger LOGGER = LogManager.getLogger(MCRXMLFunctions.class);

    private static MCRCache<String, Boolean> DISPLAY_DERIVATE_CACHE = new MCRCache<>(10000,
            "Derivate display value cache");

    //use holder to not initialize MCRXMLMetadataManager to early (simplifies junit testing)
    private static class MCRXMLMetaDataManagerHolder {
        public static final MCRXMLMetadataManager instance = MCRXMLMetadataManager.instance();
    }

    private static class MCRCategLinkServiceHolder {
        public static final MCRCategLinkService instance = MCRCategLinkServiceFactory.getInstance();
    }

    /**
     * returns the given String trimmed
     *
     * @param arg0
     *            String to be trimmed
     * @return trimmed copy of arg0
     * @see java.lang.String#trim()
     */
    public static String trim(String arg0) {
        return arg0.trim();
    }

    /**
     * returns the given String in unicode NFC normal form.
     *
     * @param arg0 String to be normalized
     * @see Normalizer#normalize(CharSequence, java.text.Normalizer.Form)
     */
    public static String normalizeUnicode(String arg0) {
        return Normalizer.normalize(arg0, Normalizer.Form.NFC);
    }

    /**
     * returns the QueryServlet-Link of the given hostAlias
     *
     * @param hostAlias
     *            remote alias
     * @return QueryServlet-Link
     */
    public static String getQueryServlet(String hostAlias) {
        return getBaseLink(hostAlias).append(CONFIG.getString(HOST_PREFIX + hostAlias + QUERY_SUFFIX)).toString();
    }

    /**
     * returns the FileNodeServlet-Link of the given hostAlias
     *
     * @param hostAlias
     *            remote alias
     * @return FileNodeServlet-Link
     */
    public static String getIFSServlet(String hostAlias) {
        return getBaseLink(hostAlias).append(CONFIG.getString(HOST_PREFIX + hostAlias + IFS_SUFFIX)).toString();
    }

    public static StringBuffer getBaseLink(String hostAlias) {
        StringBuffer returns = new StringBuffer();
        returns.append(CONFIG.getString(HOST_PREFIX + hostAlias + PROTOCOLL_SUFFIX, "http")).append("://")
                .append(CONFIG.getString(HOST_PREFIX + hostAlias + HOST_SUFFIX));
        String port = CONFIG.getString(HOST_PREFIX + hostAlias + PORT_SUFFIX, DEFAULT_PORT);
        if (!port.equals(DEFAULT_PORT)) {
            returns.append(":").append(port);
        }
        return returns;
    }

    public static String formatISODate(String isoDate, String simpleFormat, String iso639Language)
            throws ParseException {
        return formatISODate(isoDate, null, simpleFormat, iso639Language);
    }

    public static String formatISODate(String isoDate, String isoFormat, String simpleFormat, String iso639Language)
            throws ParseException {
        return formatISODate(isoDate, isoFormat, simpleFormat, iso639Language, TimeZone.getDefault().getID());
    }

    public static String formatISODate(String isoDate, String isoFormat, String simpleFormat, String iso639Language,
            String timeZone) throws ParseException {
        if (LOGGER.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder("isoDate=");
            sb.append(isoDate).append(", simpleFormat=").append(simpleFormat).append(", isoFormat=")
                    .append(isoFormat).append(", iso649Language=").append(iso639Language).append(", timeZone=")
                    .append(timeZone);
            LOGGER.debug(sb.toString());
        }
        Locale locale = new Locale(iso639Language);
        MCRISO8601Date mcrdate = new MCRISO8601Date();
        mcrdate.setFormat(isoFormat);
        mcrdate.setDate(isoDate);
        try {
            String formatted = mcrdate.format(simpleFormat, locale, timeZone);
            return formatted == null ? "?" + isoDate + "?" : formatted;
        } catch (RuntimeException iae) {
            LOGGER.error("Unable to format date " + mcrdate.getISOString() + " to " + simpleFormat + " with locale "
                    + locale + " and timezone " + timeZone, iae);
            return "?";
        }
    }

    public static String getISODate(String simpleDate, String simpleFormat, String isoFormat)
            throws ParseException {
        Date date;
        if (simpleFormat.equals("long")) {
            date = new Date(Long.parseLong(simpleDate));
        } else {
            SimpleDateFormat df = new SimpleDateFormat(simpleFormat, Locale.ROOT);
            df.setTimeZone(TimeZone.getTimeZone("UTC"));
            // or else testcase
            // "1964-02-24" would
            // result "1964-02-23"
            date = df.parse(simpleDate);
        }
        return getISODate(date, isoFormat);
    }

    public static String getISODate(Date date, String isoFormat) {
        MCRISO8601Date mcrdate = new MCRISO8601Date();
        mcrdate.setDate(date);
        mcrdate.setFormat(isoFormat);
        return mcrdate.getISOString();
    }

    public static String getISODate(String simpleDate, String simpleFormat) throws ParseException {
        return getISODate(simpleDate, simpleFormat, null);
    }

    /**
     * The method get a date String in format yyyy-MM-ddThh:mm:ssZ for ancient date values.
     *
     * @param date_value the date string
     * @param field_name the name of field of MCRMetaHistoryDate, it should be 'von' or 'bis'
     * @param calendar_name the name if the calendar defined in MCRCalendar
     * @return the date in format yyyy-MM-ddThh:mm:ssZ
     */
    public static String getISODateFromMCRHistoryDate(String date_value, String field_name, String calendar_name)
            throws ParseException {
        String formatted_date = "";
        if (field_name == null || field_name.trim().length() == 0) {
            return "";
        }
        boolean use_last_value = false;
        if (field_name.equals("bis"))
            use_last_value = true;
        try {
            Calendar calendar = MCRCalendar.getHistoryDateAsCalendar(date_value, use_last_value, calendar_name);
            GregorianCalendar g_calendar = MCRCalendar.getGregorianCalendarOfACalendar(calendar);
            formatted_date = MCRCalendar.getCalendarDateToFormattedString(g_calendar, "yyyy-MM-dd")
                    + "T00:00:00.000Z";
            if (g_calendar.get(GregorianCalendar.ERA) == GregorianCalendar.BC)
                formatted_date = "-" + formatted_date;
        } catch (Exception e) {
            if (LOGGER.isDebugEnabled()) {
                e.printStackTrace();
            }
            LOGGER.warn("Error while converting date string :" + date_value + " - " + use_last_value + " - "
                    + calendar_name);
            return "";
        }
        return formatted_date;
    }

    /**
     * Returns a string representing the current date. One can customize the
     * format of the returned string by providing a proper value for the format
     * parameter. If null or an invalid format is provided the default format
     * "yyyy-MM-dd" will be used.
     *
     * @param format
     *            the format in which the date should be formatted
     * @return the current date in the desired format
     * @see SimpleDateFormat format description
     */
    public static String getCurrentDate(String format) {
        SimpleDateFormat sdf = null;
        try {
            sdf = new SimpleDateFormat(format, Locale.ROOT);
        } catch (Exception i) {
            LOGGER.warn("Could not parse date format string, will use default \"yyyy-MM-dd\"", i);
            sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.ROOT);
        }

        return sdf.format(new Date());
    }

    /**
     * A delegate for {@link String#compareTo(String)}.
     *
     * @return s1.compareTo(s2)
     */
    public static int compare(String s1, String s2) {
        return s1.compareTo(s2);
    }

    /**
     * @param source the source string to operate on
     * @param regex the regular expression to apply
     */
    public static String regexp(String source, String regex, String replace) {
        String regexApplied = null;
        try {
            regexApplied = source.replaceAll(regex, replace);
        } catch (Exception e) {
            LOGGER.warn("Could not apply regular expression. Returning source string (" + source + ").");
            return source;
        }

        return regexApplied;
    }

    public static boolean classAvailable(String className) {
        try {
            Class.forName(className);
            LOGGER.debug("found class: " + className);
            return true;
        } catch (ClassNotFoundException e) {
            LOGGER.debug("did not find class: " + className);
            return false;
        }
    }

    public static boolean resourceAvailable(String resourceName) {
        URL resource = MCRConfigurationDir.getConfigResource(resourceName);
        if (resource == null) {
            LOGGER.debug("did not find resource: " + resourceName);
            return false;
        }
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug(MessageFormat.format("resource: {0} found at {1}", resourceName, resource.toString()));
        }
        return true;
    }

    /**
     * Encodes the given url so that one can safely embed that string in a part
     * of an URI
     *
     * @return the encoded source
     */
    public static String encodeURL(String source, String encoding) {
        String result = null;
        try {
            result = URLEncoder.encode(source, encoding);
        } catch (UnsupportedEncodingException e) {
            LOGGER.error(e);
        }
        return result;
    }

    /**
     * Encodes the given URL so, that it is a valid RFC 2396 URL.
     */
    public static String normalizeAbsoluteURL(String url) throws MalformedURLException, URISyntaxException {
        try {
            return new URI(url).toASCIIString();
        } catch (Exception e) {
            URL testURL = new URL(url);
            URI uri = new URI(testURL.getProtocol(), testURL.getUserInfo(), testURL.getHost(), testURL.getPort(),
                    testURL.getPath(), testURL.getQuery(), testURL.getRef());
            return uri.toASCIIString();
        }
    }

    /**
     * Encodes the path so that it can be safely used in an URI.
     * Same as calling {@link #encodeURIPath(String, boolean)} with boolean parameter set to false.
     * @return encoded path as described in RFC 2396
     */
    public static String encodeURIPath(String path) throws URISyntaxException {
        return encodeURIPath(path, false);
    }

    /**
     * Encodes the path so that it can be safely used in an URI.
     * @param asciiOnly
     *          if true, return only ASCII characters (e.g. encode umlauts)
     * @return encoded path as described in RFC 2396
     */
    public static String encodeURIPath(String path, boolean asciiOnly) throws URISyntaxException {
        URI relativeURI = new URI(null, null, path, null, null);
        return asciiOnly ? relativeURI.toASCIIString() : relativeURI.getRawPath();
    }

    /**
     * Decodes the path so that it can be displayed without encoded octets.
     * @param path
     *            encoded path as described in RFC 2396
     * @return decoded path
     */
    public static String decodeURIPath(String path) throws URISyntaxException {
        URI relativeURI = new URI(path);
        return relativeURI.getPath();
    }

    public static boolean isDisplayedEnabledDerivate(String derivateId) {
        MCRObjectID derId = MCRObjectID.getInstance(derivateId);
        ModifiedHandle modifiedHandle = MCRXMLMetaDataManagerHolder.instance.getLastModifiedHandle(derId, 30,
                TimeUnit.SECONDS);
        Boolean result;
        try {
            result = DISPLAY_DERIVATE_CACHE.getIfUpToDate(derivateId, modifiedHandle);
        } catch (IOException e) {
            LOGGER.warn("Error while determining when " + derId + " was last modified.", e);
            return false;
        }
        if (result != null) {
            return result;
        }
        MCRDerivate der;
        try {
            org.jdom2.Document derDoc = MCRXMLMetaDataManagerHolder.instance.retrieveXML(derId);
            if (derDoc == null) {
                LOGGER.error("Derivate \"" + derId + "\" does not exist");
                return false;
            }
            der = new MCRDerivate(derDoc);
        } catch (SAXException | JDOMException | IOException | RuntimeException e) {
            LOGGER.warn("Error while loading derivate: " + derId, e);
            return false;
        }
        org.jdom2.Element derivateElem = der.getDerivate().createXML();
        String display = derivateElem.getAttributeValue("display", "true");
        Boolean returnValue = Boolean.valueOf(display);
        DISPLAY_DERIVATE_CACHE.put(derivateId, returnValue);
        return returnValue;
    }

    /**
     * @return true if the given object is allowed for urn assignment
     */
    public static boolean isAllowedObjectForURNAssignment(String objId) {
        if (objId == null) {
            return false;
        }
        try {
            MCRObjectID obj = MCRObjectID.getInstance(objId);
            String type = obj.getTypeId();
            return isAllowedObject(type);

        } catch (Exception ex) {
            LOGGER.error("Error while checking object " + objId + " is allowed for urn assignment");
            return false;
        }
    }

    /**
     * Reads the property "URN.Enabled.Objects".
     *
     * @param givenType
     *            the type of the mycore object to check
     * @return <code>true</code> if the given type is in the list of allowed
     *         objects, <code>false</code> otherwise
     */
    private static boolean isAllowedObject(String givenType) {
        if (givenType == null)
            return false;

        String propertyName = "MCR.URN.Enabled.Objects";
        String propertyValue = MCRConfiguration.instance().getString(propertyName, null);
        if (propertyValue == null || propertyValue.length() == 0) {
            LOGGER.info("URN assignment disabled as the property \"" + propertyName + "\" is not set");
            return false;
        }

        String[] allowedTypes = propertyValue.split(",");
        for (String current : allowedTypes) {
            if (current.trim().equals(givenType.trim())) {
                return true;
            }
        }
        LOGGER.info("URN assignment disabled as the object type " + givenType
                + " is not in the list of allowed objects. See property \"" + propertyName + "\"");
        return false;
    }

    /**
     * @param objectId
     *            the id of the derivate owner
     * @return <code>true</code> if the derivate owner has a least one derivate
     *         with the display attribute set to true, <code>false</code>
     *         otherwise
     */
    public static boolean hasDisplayableDerivates(String objectId) throws Exception {
        return Optional.of(MCRObjectID.getInstance(objectId)).filter(MCRMetadataManager::exists)
                .map(MCRMetadataManager::retrieveMCRObject).map(MCRObject::getStructure)
                .map(MCRObjectStructure::getDerivates).map(List::stream)
                .map(s -> s.map(MCRMetaLinkID::getXLinkHrefID).map(MCRMetadataManager::retrieveMCRDerivate)
                        .map(MCRDerivate::getDerivate).anyMatch(MCRObjectDerivate::isDisplayEnabled))
                .orElse(false);
    }

    /**
     * Returns a list of link targets of a given MCR object type. The structure
     * is <em>link</em>. If no links are found an empty NodeList is returned.
     * 
     * @param mcrid
     *            MCRObjectID as String as the link source
     * @param destinationType
     *            MCR object type
     * @return a NodeList with <em>link</em> elements
     */
    public static NodeList getLinkDestinations(String mcrid, String destinationType) {
        DocumentBuilder documentBuilder = MCRDOMUtils.getDocumentBuilderUnchecked();
        try {
            Document document = documentBuilder.newDocument();
            Element rootElement = document.createElement("linklist");
            document.appendChild(rootElement);
            MCRLinkTableManager ltm = MCRLinkTableManager.instance();
            for (String id : ltm.getDestinationOf(mcrid, destinationType)) {
                Element link = document.createElement("link");
                link.setTextContent(id);
                rootElement.appendChild(link);
            }
            return rootElement.getChildNodes();
        } finally {
            MCRDOMUtils.releaseDocumentBuilder(documentBuilder);
        }
    }

    /**
     * Returns a list of link sources of a given MCR object type. The structure
     * is <em>link</em>. If no links are found an empty NodeList is returned.
     *
     * @param mcrid
     *            MCRObjectID as String as the link target
     * @param sourceType
     *            MCR object type
     * @return a NodeList with <em>link</em> elements
     */
    public static NodeList getLinkSources(String mcrid, String sourceType) {
        DocumentBuilder documentBuilder = MCRDOMUtils.getDocumentBuilderUnchecked();
        try {
            Document document = documentBuilder.newDocument();
            Element rootElement = document.createElement("linklist");
            document.appendChild(rootElement);
            MCRLinkTableManager ltm = MCRLinkTableManager.instance();
            for (String id : ltm.getSourceOf(mcrid)) {
                if (sourceType == null || MCRObjectID.getIDParts(id)[1].equals(sourceType)) {
                    Element link = document.createElement("link");
                    link.setTextContent(id);
                    rootElement.appendChild(link);
                }
            }
            return rootElement.getChildNodes();
        } finally {
            MCRDOMUtils.releaseDocumentBuilder(documentBuilder);
        }
    }

    /**
     * same as {@link #getLinkSources(String, String)} with
     * <code>sourceType</code>=<em>null</em>
     *
     */
    public static NodeList getLinkSources(String mcrid) {
        return getLinkSources(mcrid, null);
    }

    /**
     * Determines the mime type for the file given by its name.
     *
     * @param f
     *            the name of the file
     * @return the mime type of the given file
     */
    public static String getMimeType(String f) {
        if (f == null) {
            return "application/octet-stream";
        }
        MimetypesFileTypeMap mTypes = new MimetypesFileTypeMap();
        return mTypes.getContentType(f.toLowerCase(Locale.ROOT));
    }

    /**
     * @return the name of the maindoc of the given derivate or null if maindoc is not set
     */
    public static String getMainDocName(String derivateId) {
        if (derivateId == null || derivateId.isEmpty()) {
            return null;
        }

        MCRObjectID objectID = MCRObjectID.getInstance(derivateId);
        if (!MCRMetadataManager.exists(objectID)) {
            return null;
        }

        return MCRMetadataManager.retrieveMCRDerivate(objectID).getDerivate().getInternals().getMainDoc();
    }

    /**
     * The method return a org.w3c.dom.NodeList as subpath of the doc input
     * NodeList selected by a path as String.
     *
     * @param doc
     *            the input org.w3c.dom.Nodelist
     * @param path
     *            the path of doc as String
     * @return a subpath of doc selected by path as org.w3c.dom.NodeList
     */
    public static NodeList getTreeByPath(NodeList doc, String path) {
        NodeList n = null;
        DocumentBuilder documentBuilder = MCRDOMUtils.getDocumentBuilderUnchecked();
        try {
            // build path selection
            XPathFactory factory = XPathFactory.newInstance();
            XPath xpath = factory.newXPath();
            XPathExpression expr = xpath.compile(path);
            // select part
            Document document = documentBuilder.newDocument();
            if (doc.item(0).getNodeName().equals("#document")) {
                // LOGGER.debug("NodeList is a document.");
                Node child = doc.item(0).getFirstChild();
                if (child != null) {
                    Node node = (Node) doc.item(0).getFirstChild();
                    Node imp = document.importNode(node, true);
                    document.appendChild(imp);
                } else {
                    document.appendChild(doc.item(0));
                }
            }
            n = (NodeList) expr.evaluate(document, XPathConstants.NODESET);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            MCRDOMUtils.releaseDocumentBuilder(documentBuilder);
        }
        return n;
    }

    /**
     * checks if the current user is in a specific role
     *
     * @param role
     *            a role name
     * @return true if user has this role
     */
    public static boolean isCurrentUserInRole(String role) {
        return MCRSessionMgr.getCurrentSession().getUserInformation().isUserInRole(role);
    }

    public static boolean isCurrentUserSuperUser() {
        return MCRSessionMgr.getCurrentSession().getUserInformation()
                .equals(MCRSystemUserInformation.getSuperUserInstance());
    }

    public static boolean isCurrentUserGuestUser() {
        return MCRSessionMgr.getCurrentSession().getUserInformation()
                .equals(MCRSystemUserInformation.getGuestInstance());
    }

    public static String getCurrentUserAttribute(String attribute) {
        return MCRSessionMgr.getCurrentSession().getUserInformation().getUserAttribute(attribute);
    }

    public static boolean exists(String objectId) {
        return MCRMetadataManager.exists(MCRObjectID.getInstance(objectId));
    }

    /**
     * Verifies if object is in specified category.
     * @see MCRCategLinkService#isInCategory(MCRCategLinkReference, MCRCategoryID)
     * @param objectId valid MCRObjectID as String
     * @param categoryId valid MCRCategoryID as String
     * @return true if object is in category, else false
     */
    public static boolean isInCategory(String objectId, String categoryId) {
        try {
            MCRCategoryID categID = MCRCategoryID.fromString(categoryId);
            MCRObjectID mcrObjectID = MCRObjectID.getInstance(objectId);
            MCRCategLinkReference reference = new MCRCategLinkReference(mcrObjectID);
            return MCRCategLinkServiceHolder.instance.isInCategory(reference, categID);
        } catch (Throwable e) {
            LOGGER.error("Error while checking if object is in category", e);
            return false;
        }
    }

    /**
     * Checks if the User-Agent is sent from a mobile device
     * @return true if the User-Agent is sent from a mobile device
     */
    public static boolean isMobileDevice(String UserAgent) {
        return UserAgent.toLowerCase(Locale.ROOT).contains("mobile");
    }

    public static boolean hasParentCategory(String classificationId, String categoryId) {
        MCRCategoryID categID = new MCRCategoryID(classificationId, categoryId);
        //root category has level 0
        return !categID.isRootID() && MCRCategoryDAOFactory.getInstance().getCategory(categID, 0).getLevel() > 1;
    }

    /**
     * @param classificationId
     * @param categoryId
     * @return
     */
    public static String getDisplayName(String classificationId, String categoryId) {
        MCRCategory category = null;
        try {
            MCRCategoryID categID = new MCRCategoryID(classificationId, categoryId);
            MCRCategoryDAO dao = MCRCategoryDAOFactory.getInstance();
            category = dao.getCategory(categID, 0);
        } catch (Exception e) {
            LOGGER.warn("Could not determine display name for classification id " + classificationId
                    + " and category id " + categoryId);
            return "";
        }

        return category.getCurrentLabel().map(MCRLabel::getText).orElse("");
    }

    /**
     * @param classificationId
     * @param categoryId
     * @return
     */
    public static boolean isCategoryID(String classificationId, String categoryId) {
        MCRCategory category = null;
        try {
            MCRCategoryID categID = MCRCategoryID.fromString(classificationId + ":" + categoryId);
            MCRCategoryDAO dao = MCRCategoryDAOFactory.getInstance();
            category = dao.getCategory(categID, 0);
        } catch (Exception e) {
            LOGGER.warn("Could not determine state for classification id " + classificationId + " and category id "
                    + categoryId);
        }

        return category == null ? false : true;
    }

    /**
     * Method returns the amount of space consumed by the files contained in the
     * derivate container. The returned string is already formatted meaning it
     * has already the optimal measurement unit attached (e.g. 142 MB, ).
     *
     * @param derivateId
     *            the derivate id for which the size should be returned
     * @return the size as formatted string
     */
    public static String getSize(String derivateId) throws IOException {
        MCRPath rootPath = MCRPath.getPath(derivateId, "/");
        final AtomicLong size = new AtomicLong();
        Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                size.addAndGet(attrs.size());
                return super.visitFile(file, attrs);
            }

        });
        return MCRUtils.getSizeFormatted(size.get());
    }

    /**
     * @param derivateID
     *            the derivate id
     * @return the id of the mycore object that contains the derivate with the
     *         given id
     */
    public static String getMCRObjectID(String derivateID) {
        return getMCRObjectID(derivateID, 5000);
    }

    /**
     * Same as {@link MCRMetadataManager#getObjectId(MCRObjectID, long, TimeUnit)} with String representation.
     */
    public static String getMCRObjectID(final String derivateID, final long expire) {
        return MCRMetadataManager.getObjectId(MCRObjectID.getInstance(derivateID), expire, TimeUnit.MILLISECONDS)
                .toString();
    }

    /**
     * @param uri the uri to resolve
     */
    public static NodeList resolve(String uri) throws JDOMException {
        org.jdom2.Element element = MCRURIResolver.instance().resolve(uri);
        element.detach();
        org.jdom2.Document document = new org.jdom2.Document(element);
        return new DOMOutputter().output(document).getDocumentElement().getChildNodes();
    }

    /**
     * Helper function for xslImport URI Resolver and {@link #hasNextImportStep(String)}
     * @param includePart substring after "xmlImport:"
     */
    public static String nextImportStep(String includePart) {
        int border = includePart.indexOf(':');
        String selfName = null;
        if (border > 0) {
            selfName = includePart.substring(border + 1);
            includePart = includePart.substring(0, border);
        }
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("get next import step for " + includePart);
        }
        // get the parameters from mycore.properties
        List<String> importList = Collections.emptyList();
        importList = MCRConfiguration.instance().getStrings("MCR.URIResolver.xslImports." + includePart,
                importList);
        if (importList.isEmpty()) {
            LOGGER.info("MCR.URIResolver.xslImports." + includePart + " has no Stylesheets defined");
        } else {
            ListIterator<String> listIterator = importList.listIterator(importList.size());

            if (selfName == null && listIterator.hasPrevious()) {
                return listIterator.previous();
            }

            while (listIterator.hasPrevious()) {
                String currentStylesheet = listIterator.previous();
                if (currentStylesheet.equals(selfName)) {
                    if (listIterator.hasPrevious()) {
                        return listIterator.previous();
                    } else {
                        LOGGER.debug("xslImport reached end of chain:" + importList);
                        return "";
                    }
                }
                //continue;
            }
            LOGGER.warn("xslImport could not find " + selfName + " in " + importList);
        }
        return "";
    }

    public static boolean hasNextImportStep(String uri) {
        boolean returns = !nextImportStep(uri).isEmpty();
        LOGGER.debug("hasNextImportStep('" + uri + "') -> " + returns);
        return returns;
    }

    public static String shortenText(String text, int lenght) {
        if (text.length() <= lenght) {
            return text;
        }
        int i = text.indexOf(' ', lenght);
        if (i < 0) {
            return text;
        }
        return text.substring(0, i) + "...";
    }

    public static String shortenPersonLabel(String text) {
        int pos = text.indexOf("(");
        if (pos == -1) {
            return text;
        }
        return text.substring(0, pos - 1);
    }

    /**
     * Return <code>true</code> if s contains HTML markup tags or entities.
     *
     * @param s string to test
     * @return true if string contains HTML
     */
    public static boolean isHtml(final String s) {
        boolean ret = false;
        if (s != null) {
            ret = HTML_MATCH_PATTERN.matcher(s).find();
        }
        return ret;
    }

    /**
     * Strippes HTML tags from string.
     * 
     * @param s string to strip HTML tags of
     * @return the plain text without tags
     */
    public static String stripHtml(final String s) {
        StringBuffer res = new StringBuffer(s);

        Matcher m;
        while ((m = TAG_PATTERN.matcher(res.toString())).find()) {
            res.delete(m.start(), m.end());
            res.insert(m.start(), stripHtml(m.group(m.groupCount() - 1)));
        }

        return StringEscapeUtils.unescapeHtml(res.toString()).replaceAll(TAG_SELF_CLOSING, "");
    }

}