cz.mzk.editor.server.fedora.utils.FedoraUtils.java Source code

Java tutorial

Introduction

Here is the source code for cz.mzk.editor.server.fedora.utils.FedoraUtils.java

Source

/*
 * Metadata Editor
 * @author Jiri Kremser
 * 
 * 
 * 
 * Metadata Editor - Rich internet application for editing metadata.
 * Copyright (C) 2011  Jiri Kremser (kremser@mzk.cz)
 * Moravian Library in Brno
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * 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; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 * 
 */

package cz.mzk.editor.server.fedora.utils;

/**
 *
 * @author incad
 */
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;

import java.nio.charset.Charset;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.xml.namespace.NamespaceContext;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import javax.inject.Inject;

import com.google.inject.name.Named;
import com.gwtplatform.dispatch.shared.ActionException;

import org.apache.commons.lang.StringEscapeUtils;
import org.apache.log4j.Logger;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import org.fedora.api.RelationshipTuple;

import cz.mzk.editor.client.ConnectionException;
import cz.mzk.editor.client.DublinCoreConstants;
import cz.mzk.editor.client.mods.ModsCollectionClient;
import cz.mzk.editor.client.util.Constants;
import cz.mzk.editor.server.config.EditorConfiguration;
import cz.mzk.editor.server.fedora.FedoraAccess;
import cz.mzk.editor.server.mods.ModsCollection;
import cz.mzk.editor.server.util.IOUtils;
import cz.mzk.editor.server.util.RESTHelper;
import cz.mzk.editor.server.util.XMLUtils;
import cz.mzk.editor.shared.domain.DigitalObjectModel;
import cz.mzk.editor.shared.domain.FedoraNamespaces;
import cz.mzk.editor.shared.domain.FedoraRelationship;
import cz.mzk.editor.shared.domain.NamedGraphModel;
import cz.mzk.editor.shared.rpc.DigitalObjectDetail;
import cz.mzk.editor.shared.rpc.DublinCore;

import static cz.mzk.editor.client.util.Constants.DATASTREAM_ID.BIBLIO_MODS;
import static cz.mzk.editor.client.util.Constants.DATASTREAM_ID.DC;
import static cz.mzk.editor.client.util.Constants.DATASTREAM_ID.RELS_EXT;
import static cz.mzk.editor.client.util.Constants.DATASTREAM_ID.TEXT_OCR;
import static cz.mzk.editor.server.fedora.utils.FoxmlUtils.LABEL_VALUE;
import static cz.mzk.editor.shared.domain.FedoraNamespaces.BIBILO_MODS_URI;
import static cz.mzk.editor.shared.domain.FedoraNamespaces.OAI_DC_NAMESPACE_URI;
import static cz.mzk.editor.shared.domain.FedoraNamespaces.RELS_EXT_NAMESPACE_URI;

// TODO: Auto-generated Javadoc
/**
 * The Class FedoraUtils.
 */
public class FedoraUtils {

    /** The Constant LOGGER. */
    private static final Logger LOGGER = Logger.getLogger(FedoraUtils.class);

    /** The Constant RELS_EXT_STREAM. */
    public static final String RELS_EXT_STREAM = "RELS-EXT";

    /** The Constant IMG_THUMB_STREAM. */
    public static final String IMG_THUMB_STREAM = "IMG_THUMB";

    /** The Constant IMG_FULL_STREAM. */
    public static final String IMG_FULL_STREAM = "IMG_FULL";

    /** The configuration. */
    @Inject
    private static EditorConfiguration configuration;

    private static final String RELS_EXT_PART_KRAM = "kramerius:";
    private static final String RDF_RESOURCE_ATTR = "rdf:resource";
    private static final String XMLNS_ATTR = "xmlns";
    private static final String XMLNS_ATTR_CONTENT = "http://www.nsdl.org/ontologies/relationships#";

    /** The Constant TERMINATOR1. */
    private static final String TERMINATOR1 = ">\n";

    /** The Constant TERMINATOR2. */
    private static final String TERMINATOR2 = ">";

    /** The Constant DC_HEAD. */
    private static final String DC_HEAD = "<oai_dc:dc xmlns:oai_dc=\"http://www.openarchives.org/OAI/2.0/oai_dc/\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd\">\n";

    /** The Constant DC_TAIL. */
    private static final String DC_TAIL = "</oai_dc:dc>";

    /** The Constant DC_PART_1. */
    private static final String DC_PART_1 = "<dc:";

    /** The Constant DC_PART_2. */
    private static final String DC_PART_2 = "</dc:";

    private static final String EMPTY_MODS = "<modsCollection xmlns=\"http://www.loc.gov/mods/v3\" xmlns:ns2=\"http://www.w3.org/1999/xlink\">\n"
            + "<mods version=\"3.4\">\n" + "</mods>\n" + "</modsCollection>";

    /** The fedora access. */
    @Inject
    @Named("securedFedoraAccess")
    private static FedoraAccess fedoraAccess;

    /** The ns context. */
    @Inject
    private static NamespaceContext nsContext;

    /** The xpfactory. */
    private static XPathFactory xpfactory;

    /**
     * Gets the rdf pids.
     * 
     * @param pid
     *        the pid
     * @param relation
     *        the relation
     * @return the rdf pids
     */
    public static ArrayList<String> getRdfPids(String pid, String relation) {
        ArrayList<String> pids = new ArrayList<String>();
        try {

            String command = configuration.getFedoraHost() + "/get/" + pid + "/" + RELS_EXT_STREAM;
            InputStream is = RESTHelper.get(command, configuration.getFedoraLogin(),
                    configuration.getFedoraPassword(), true);
            Document contentDom = XMLUtils.parseDocument(is);
            XPathFactory factory = XPathFactory.newInstance();
            XPath xpath = factory.newXPath();
            String xPathStr = "/RDF/Description/" + relation;
            XPathExpression expr = xpath.compile(xPathStr);
            NodeList nodes = (NodeList) expr.evaluate(contentDom, XPathConstants.NODESET);
            for (int i = 0; i < nodes.getLength(); i++) {
                Node childnode = nodes.item(i);
                if (!childnode.getNodeName().contains("hasModel")) {
                    pids.add(childnode.getNodeName() + " "
                            + childnode.getAttributes().getNamedItem("rdf:resource").getNodeValue().split("/")[1]);
                }
            }
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }
        return pids;
    }

    /**
     * Gets the subject pids.
     * 
     * @param objectPid
     *        the object pid
     * @return the subject pids
     */
    public static List<RelationshipTuple> getSubjectPids(String objectPid) {
        return getSubjectOrObjectPids("*%20*%20%3Cinfo:fedora/" + objectPid + "%3E");
    }

    /**
     * Gets the object pids.
     * 
     * @param subjectPid
     *        the subject pid
     * @return the object pids
     */
    public static List<RelationshipTuple> getObjectPids(String subjectPid) {
        return getSubjectOrObjectPids("%3Cinfo:fedora/" + subjectPid + "%3E%20*%20*");
    }

    private static List<RelationshipTuple> getSubjectOrObjectPids(String restOfCommand) {
        List<RelationshipTuple> retval = new ArrayList<RelationshipTuple>();
        String command = configuration.getFedoraHost() + "/risearch?type=triples&lang=spo&format=N-Triples&query="
                + restOfCommand;
        InputStream stream = null;
        try {
            stream = RESTHelper.get(command, configuration.getFedoraLogin(), configuration.getFedoraPassword(),
                    true);
            if (stream == null)
                return null;
            String result = IOUtils.readAsString(stream, Charset.forName("UTF-8"), true);
            String[] lines = result.split("\n");
            for (String line : lines) {
                String[] tokens = line.split(" ");
                if (tokens.length < 3)
                    continue;
                try {
                    RelationshipTuple tuple = new RelationshipTuple();
                    tuple.setSubject(tokens[0].substring(1, tokens[0].length() - 1));
                    tuple.setPredicate(tokens[1].substring(1, tokens[1].length() - 1));
                    tuple.setObject(tokens[2].substring(1, tokens[2].length() - 1));
                    tuple.setIsLiteral(false);
                    retval.add(tuple);
                } catch (Exception ex) {
                    LOGGER.info("Problem parsing RDF, skipping line:" + Arrays.toString(tokens) + " : " + ex);
                }
            }
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        } finally {
            if (stream != null)
                try {
                    stream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
        return retval;
    }

    /**
     * Find first page pid.
     * 
     * @param pid
     *        the pid
     * @return the string
     */
    public static String findFirstPagePid(String pid) {

        ArrayList<String> pids = new ArrayList<String>();
        try {
            String command = configuration.getFedoraHost() + "/get/" + pid + "/RELS-EXT";
            InputStream is = RESTHelper.get(command, configuration.getFedoraLogin(),
                    configuration.getFedoraPassword(), true);
            Document contentDom = XMLUtils.parseDocument(is);
            XPathFactory factory = XPathFactory.newInstance();
            XPath xpath = factory.newXPath();
            XPathExpression expr = xpath.compile("/RDF/Description/*");
            NodeList nodes = (NodeList) expr.evaluate(contentDom, XPathConstants.NODESET);
            for (int i = 0; i < nodes.getLength(); i++) {
                Node childnode = nodes.item(i);
                String nodeName = childnode.getNodeName();
                if (nodeName.contains(FedoraRelationship.hasPage.getStringRepresentation())
                        || nodeName.contains(FedoraRelationship.isOnPage.getStringRepresentation())) {
                    return childnode.getAttributes().getNamedItem("rdf:resource").getNodeValue().split("uuid:")[1];
                } else if (!nodeName.contains("hasModel") && childnode.hasAttributes()
                        && childnode.getAttributes().getNamedItem("rdf:resource") != null) {
                    pids.add(childnode.getAttributes().getNamedItem("rdf:resource").getNodeValue().split("/")[1]);
                }
            }
            for (String relpid : pids) {
                return FedoraUtils.findFirstPagePid(relpid);
            }
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }
        return null;
    }

    /**
     * Vraci url na stream s DJVU.
     * 
     * @param uuid
     *        objektu
     * @return the dj vu image
     */
    public static String getDjVuImage(String uuid) {
        String imagePath = configuration.getFedoraHost() + "/get/" + uuid + "/" + IMG_FULL_STREAM;
        return imagePath;
    }

    /**
     * Vraci url na stream THUMB.
     * 
     * @param uuid
     *        the uuid
     * @return the thumbnail from fedora
     */
    public static String getThumbnailFromFedora(String uuid) {
        String imagePath = configuration.getFedoraHost() + "/get/" + uuid + "/" + IMG_THUMB_STREAM;
        return imagePath;
    }

    /**
     * Gets the fedora datastreams list.
     * 
     * @param uuid
     *        the uuid
     * @return the fedora datastreams list
     */
    public static String getFedoraDatastreamsList(String uuid) {
        String datastreamsListPath = configuration.getFedoraHost() + "/objects/" + uuid + "/datastreams?format=xml";
        return datastreamsListPath;
    }

    /**
     * Removes the elements.
     * 
     * @param parent
     *        the parent
     * @param doc
     *        the doc
     * @throws XPathExpressionException
     */
    public static List<Element> removeElements(Element parent, Document doc, String xPath)
            throws XPathExpressionException {
        XPathExpression expr = makeNSAwareXpath().compile(xPath);
        NodeList nodes = null;
        List<Element> removedNodes = null;
        try {
            nodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
            removedNodes = new ArrayList<Element>(nodes.getLength());
            for (int i = 0, lastIndex = nodes.getLength() - 1; i <= lastIndex; i++) {
                removedNodes.add((Element) parent.removeChild(nodes.item(i)));
            }
        } catch (XPathExpressionException e) {
            LOGGER.error("Unable to remove elements", e);
        }
        return removedNodes;
    }

    /**
     * Make ns aware xpath.
     * 
     * @return the x path
     */
    public static XPath makeNSAwareXpath() {
        if (xpfactory == null) {
            xpfactory = XPathFactory.newInstance();
        }
        XPath xpath = xpfactory.newXPath();
        xpath.setNamespaceContext(nsContext);
        return xpath;
    }

    /**
     * Create new relations part.
     * 
     * @param detail
     *        the detail
     * @return content content of the new relations part
     */
    public static String createNewRealitonsPart(DigitalObjectDetail detail) {
        if (detail.getAllItems() == null)
            return null;

        Document relsExt = null;

        try {
            relsExt = fedoraAccess.getRelsExt(detail.getUuid());
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            XPathExpression all = makeNSAwareXpath().compile("/rdf:RDF/rdf:Description");
            NodeList nodes1 = (NodeList) all.evaluate(relsExt, XPathConstants.NODESET);
            Element parent = null;
            if (nodes1.getLength() != 0) {
                parent = (Element) nodes1.item(0);
            }

            int modelId = 0;
            boolean changed = false;
            List<DigitalObjectModel> models = NamedGraphModel.getChildren(detail.getModel());
            for (List<DigitalObjectDetail> data : detail.getAllItems()) {
                if (data != null) { // is changed
                    changed = true;
                    String xPathString = NamedGraphModel.getRelationship(detail.getModel(), models.get(modelId))
                            .getXPathNamespaceAwareQuery();
                    removeElements(parent, relsExt, xPathString);
                }
                modelId++;
            }
            if (!changed)
                return null;

            boolean lameNS = false;
            String rdfDescXPath = "/rdf:RDF/rdf:Description";
            Element rdfDescEl = FoxmlUtils.getElement(relsExt, rdfDescXPath);
            Element policyEl = null;
            if (rdfDescEl != null && rdfDescEl.getElementsByTagName("policy").getLength() > 0)
                policyEl = (Element) rdfDescEl.getElementsByTagName("policy").item(0);
            if (policyEl != null && policyEl.getAttribute(XMLNS_ATTR) != null) {
                lameNS = policyEl.getAttribute("xmlns").equals(FedoraNamespaces.KRAMERIUS_URI);
            }
            int i = 0;
            for (List<DigitalObjectDetail> data : detail.getAllItems()) {
                FedoraRelationship relationship = NamedGraphModel.getRelationship(detail.getModel(), models.get(i));
                if (data != null) {
                    String relation = relationship.getStringRepresentation();
                    for (DigitalObjectDetail obj : data) {

                        Element newEl = relsExt.createElement((lameNS ? "" : RELS_EXT_PART_KRAM).concat(relation));
                        if (lameNS)
                            newEl.setAttribute(XMLNS_ATTR, XMLNS_ATTR_CONTENT);
                        newEl.setAttribute(RDF_RESOURCE_ATTR, Constants.FEDORA_INFO_PREFIX + obj.getUuid());

                        rdfDescEl.appendChild(newEl);
                    }
                } else {
                    List<Element> removedElements = removeElements(parent, relsExt,
                            relationship.getXPathNamespaceAwareQuery());
                    for (Element el : removedElements)
                        rdfDescEl.appendChild(el);
                }
                i++;
            }

        } catch (XPathExpressionException e) {
            LOGGER.warn("XPath failure", e);
        }

        String content = null;
        try {
            content = getStringFromDocument(relsExt, true);
        } catch (TransformerException e) {
            LOGGER.warn("Document transformer failure", e);
            e.printStackTrace();
        }
        return content;
    }

    /**
     * Append dc element.
     * 
     * @param contentBuilder
     *        the content builder
     * @param values
     *        the values
     * @param elementName
     *        the element name
     */
    private static void appendDCElement(StringBuilder contentBuilder, List<String> values, String elementName) {
        if (values != null && values.size() > 0) {
            for (String value : values) {
                value = StringEscapeUtils.escapeXml(value);
                contentBuilder.append(DC_PART_1).append(elementName).append(TERMINATOR2).append(value)
                        .append(DC_PART_2).append(elementName).append(TERMINATOR1);
            }
        }
    }

    /**
     * Create new dublin core part.
     * 
     * @param dc
     *        the dublin core
     * @return content the content of the new dublin core part.
     */
    public static String createNewDublinCorePart(DublinCore dc) {
        if (dc != null) {
            StringBuilder contentBuilder = new StringBuilder();
            contentBuilder.append(DC_HEAD);
            appendDCElement(contentBuilder, dc.getContributor(), DublinCoreConstants.DC_CONTRIBUTOR);
            appendDCElement(contentBuilder, dc.getCoverage(), DublinCoreConstants.DC_COVERAGE);
            appendDCElement(contentBuilder, dc.getCreator(), DublinCoreConstants.DC_CREATOR);
            appendDCElement(contentBuilder, dc.getDate(), DublinCoreConstants.DC_DATE);
            appendDCElement(contentBuilder, dc.getDescription(), DublinCoreConstants.DC_DESCRIPTION);
            appendDCElement(contentBuilder, dc.getFormat(), DublinCoreConstants.DC_FORMAT);
            appendDCElement(contentBuilder, dc.getIdentifier(), DublinCoreConstants.DC_IDENTIFIER);
            appendDCElement(contentBuilder, dc.getLanguage(), DublinCoreConstants.DC_LANGUAGE);
            appendDCElement(contentBuilder, dc.getPublisher(), DublinCoreConstants.DC_PUBLISHER);
            appendDCElement(contentBuilder, dc.getRelation(), DublinCoreConstants.DC_RELATION);
            appendDCElement(contentBuilder, dc.getRights(), DublinCoreConstants.DC_RIGHTS);
            appendDCElement(contentBuilder, dc.getSource(), DublinCoreConstants.DC_SOURCE);
            appendDCElement(contentBuilder, dc.getSubject(), DublinCoreConstants.DC_SUBJECT);
            appendDCElement(contentBuilder, dc.getTitle(), DublinCoreConstants.DC_TITLE);
            appendDCElement(contentBuilder, dc.getType(), DublinCoreConstants.DC_TYPE);
            contentBuilder.append(DC_TAIL);

            String content = contentBuilder.toString();
            return content;
        }
        return null;
    }

    /**
     * Create new mods part.
     * 
     * @param modsClient
     *        the modsClient
     * @return content the content of the new mods part
     */
    public static String createNewModsPart(ModsCollectionClient modsClient) {
        if (modsClient != null) {
            ModsCollection mods = BiblioModsUtils.toMods(modsClient);
            return mods == null ? EMPTY_MODS : BiblioModsUtils.toXML(mods);
        }
        return null;
    }

    /**
     * @return String[], The array of Strings which contains a working copy of
     *         FOXML and individual datastreams in this order: String[0] =
     *         FOXML, String[1] = FOXML without the last version of the changed
     *         datastreams, String[2] = DC datastream, String[3] = MODS
     *         datastream, String[4] = RELS-EXT datastream.
     */
    public static String[] createWorkingCopyFoxmlAndStreams(DigitalObjectDetail detail, boolean onlyFoxml)
            throws ActionException {

        String[] stringsWithXml = new String[] { null, null, null, null, null };
        Document[] documentsWithXml = new Document[] { null, null, null, null, null };

        try {
            documentsWithXml[0] = FoxmlUtils.getFoxmlDocument(fedoraAccess.getFOXMLInputStream(detail.getUuid()));
        } catch (IOException e) {
            throw new ActionException();
        }

        if (detail.isLabelChanged()) {
            modifyLabel(detail, documentsWithXml[0]);
        }
        if (detail.isDcChanged()) {
            stringsWithXml[2] = createNewDublinCorePart(detail.getDc());
            modifyStream(documentsWithXml[0], DC.getValue(), stringsWithXml[2]);
            stringsWithXml[2] = Constants.XML_HEADER_WITH_BACKSLASHES + stringsWithXml[2];

        } else if (!onlyFoxml) {
            try {
                documentsWithXml[2] = fedoraAccess.getDC(detail.getUuid());
            } catch (IOException e) {
                throw new ActionException();
            }
        }

        if (detail.isModsChanged()) {
            stringsWithXml[3] = createNewModsPart(detail.getMods());
            modifyStream(documentsWithXml[0], BIBLIO_MODS.getValue(), stringsWithXml[3]);
            stringsWithXml[3] = Constants.XML_HEADER_WITH_BACKSLASHES + stringsWithXml[3];

        } else if (!onlyFoxml) {
            try {
                documentsWithXml[3] = fedoraAccess.getBiblioMods(detail.getUuid());
            } catch (IOException e) {
                throw new ActionException();
            }
        }

        if (detail.getAllItems() != null) {
            stringsWithXml[4] = createNewRealitonsPart(detail);
            modifyStream(documentsWithXml[0], RELS_EXT.getValue(), stringsWithXml[4]);
            stringsWithXml[4] = Constants.XML_HEADER_WITH_BACKSLASHES + stringsWithXml[4];

        } else if (!onlyFoxml) {
            try {
                documentsWithXml[4] = fedoraAccess.getRelsExt(detail.getUuid());
            } catch (IOException e) {
                throw new ActionException();
            }
        }

        if (detail.isOcrChanged()) {
            String newContent = detail.getOcr();
            modifyStream(documentsWithXml[0], TEXT_OCR.getValue(), newContent);
        } else if (detail.thereWasAnyOcr() && !onlyFoxml) {
            String oldContent = fedoraAccess.getOcr(detail.getUuid());
            addOldOcr(documentsWithXml[0], oldContent);
        }

        return createStringsArray(detail, onlyFoxml, stringsWithXml, documentsWithXml);
    }

    /**
     * @param document
     */
    public static String getStringFromDocument(Document document, boolean omitXmlDeclaration)
            throws TransformerException {
        TransformerFactory transFactory = TransformerFactory.newInstance();
        Transformer transformer = transFactory.newTransformer();
        StringWriter buffer = new StringWriter();
        if (omitXmlDeclaration) {
            transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
        }
        transformer.transform(new DOMSource(document), new StreamResult(buffer));
        return buffer.toString();
    }

    private static String[] createStringsArray(DigitalObjectDetail detail, boolean onlyFoxml,
            String[] stringsWithXml, Document[] documentsWithXml) {

        int creatingLength = (onlyFoxml ? 1 : stringsWithXml.length);

        for (int i = 0; i < creatingLength; i++) {
            if (stringsWithXml[i] == null && documentsWithXml[i] != null) {
                try {
                    stringsWithXml[i] = getStringFromDocument(documentsWithXml[i], false);
                    ;
                    if (i == 0 && !onlyFoxml) {
                        try {
                            documentsWithXml[1] = FoxmlUtils.getFoxmlDocument(
                                    new ByteArrayInputStream(stringsWithXml[i].getBytes("UTF-8")));
                        } catch (IOException e) {
                            LOGGER.warn("IO failure", e);
                        }
                        if (detail.isDcChanged()) {
                            removeNextToLastVersion(documentsWithXml[1], DC.getValue());
                        }
                        if (detail.isModsChanged()) {
                            removeNextToLastVersion(documentsWithXml[1], BIBLIO_MODS.getValue());
                        }
                        if (detail.getAllItems() != null) {
                            removeNextToLastVersion(documentsWithXml[1], RELS_EXT.getValue());
                        }
                        if (detail.isOcrChanged()) {
                            removeNextToLastVersion(documentsWithXml[1], TEXT_OCR.getValue());
                        }
                    }
                } catch (TransformerException e) {
                    LOGGER.warn("Document transformer failure", e);
                }
            }
        }
        return stringsWithXml;
    }

    /**
     * @param foxmlDocument
     * @param oldContent
     */
    private static void addOldOcr(Document foxmlDocument, String oldContent) {
        try {
            String lastContLocXPath = "//foxml:datastream[@ID=\'" + TEXT_OCR.getValue()
                    + "\']/foxml:datastreamVersion[last()]/foxml:contentLocation[last()]";

            XPathExpression all = makeNSAwareXpath().compile(lastContLocXPath);
            NodeList listOfstream = (NodeList) all.evaluate(foxmlDocument, XPathConstants.NODESET);
            Element lastContLocElement = null;
            if (listOfstream.getLength() != 0) {
                lastContLocElement = (Element) listOfstream.item(0);
            }
            Element localContElement = foxmlDocument.createElement("foxml:content");

            localContElement.setTextContent(oldContent);
            lastContLocElement.appendChild(localContElement);

        } catch (XPathExpressionException e) {
            LOGGER.warn("XPath failure", e);
        }
    }

    private static int getVersionNumber(String id) {
        String[] splitedId = id.split("\\.");
        return Integer.parseInt(splitedId[1]);
    }

    /**
     * @param foxmlDocument
     * @param streamToModify
     * @param newContent
     */
    private static void modifyStream(Document foxmlDocument, String streamToModify, String newContent) {

        if (newContent != null) {
            try {
                Element versionElement = foxmlDocument.createElement("foxml:datastreamVersion");

                if (streamToModify.equals(RELS_EXT.getValue())) {
                    versionElement.setAttribute("LABEL", "RDF Statements about this object");
                    versionElement.setAttribute("FORMAT_URI", RELS_EXT_NAMESPACE_URI);
                    versionElement.setAttribute("MIMETYPE", "application/rdf+xml");

                } else {
                    versionElement.setAttribute("MIMETYPE", "text/xml");

                    if (streamToModify.equals(DC.getValue())) {
                        versionElement.setAttribute("LABEL", "Dublin Core Record for this object");
                        versionElement.setAttribute("FORMAT_URI", OAI_DC_NAMESPACE_URI);

                    } else if (streamToModify.equals(BIBLIO_MODS.getValue())) {
                        versionElement.setAttribute("LABEL", "BIBLIO_MODS description of current object");
                        versionElement.setAttribute("FORMAT_URI", BIBILO_MODS_URI);

                    } else if (streamToModify.equals(TEXT_OCR.getValue())) {
                        versionElement.setAttribute("LABEL", "");
                        Element contLocElement = foxmlDocument.createElement("foxml:contentLocation");
                        contLocElement.setAttribute("TYPE", "INTERNAL_ID");
                        contLocElement.setAttribute("REF", "LOCAL");
                        Element localContElement = foxmlDocument.createElement("foxml:content");
                        localContElement.setTextContent(newContent);
                        contLocElement.appendChild(localContElement);
                        versionElement.appendChild(contLocElement);
                    }
                }

                String lastStreamXPath = "//foxml:datastream[@ID=\'" + streamToModify
                        + "\']/foxml:datastreamVersion[last()]";
                Element element = FoxmlUtils.getElement(foxmlDocument, lastStreamXPath);

                int versionNumber = 0;
                if (element != null) {
                    versionNumber = getVersionNumber(element.getAttribute("ID"));
                }

                versionElement.setAttribute("ID", streamToModify + "." + (versionNumber + 1));
                versionElement.setAttribute("CREATED", "NOT YET");
                versionElement.setAttribute("SIZE", "0");

                Element contentElement = foxmlDocument.createElement("foxml:xmlContent");

                try {
                    InputStream is = new ByteArrayInputStream(newContent.getBytes("UTF-8"));
                    Document newStreamDocument = FoxmlUtils.getFoxmlDocument(is);
                    NodeList streamNodeList = newStreamDocument.getChildNodes();
                    for (int i = 0; i < streamNodeList.getLength(); i++) {
                        Node myNewNode = foxmlDocument.importNode(streamNodeList.item(i), true);
                        contentElement.appendChild(myNewNode);
                    }
                } catch (IOException e) {
                    System.err.println("IO fauilure" + e);
                }

                versionElement.appendChild(contentElement);

                String streamXPath = "//foxml:datastream[@ID=\'" + streamToModify + "\']";
                Element parentOfStream = FoxmlUtils.getElement(foxmlDocument, streamXPath);

                if (parentOfStream == null) {
                    String digObjXPath = "//foxml:digitalObject";
                    Element digObjElement = FoxmlUtils.getElement(foxmlDocument, digObjXPath);

                    parentOfStream = foxmlDocument.createElement("datastream");
                    parentOfStream.setAttribute("ID", streamToModify);
                    parentOfStream.setAttribute("STATE", "A");
                    //TODO for other streams if necessary
                    if (streamToModify.equals(TEXT_OCR.getValue()))
                        parentOfStream.setAttribute("CONTROL_GROUP", "M");
                    parentOfStream.setAttribute("VERSIONABLE", "false");
                    digObjElement.appendChild(parentOfStream);
                }

                parentOfStream.appendChild(versionElement);

            } catch (XPathExpressionException e) {
                LOGGER.warn("XPath failure", e);
            }
        }
    }

    private static void removeNextToLastVersion(Document foxmlDocument, String streamToModify) {

        String nextToLastStreamXPath = "/foxml:digitalObject//foxml:datastream[@ID=\'" + streamToModify
                + "\']/foxml:datastreamVersion[position()=last()-1]";

        String streamXPath = "//foxml:datastream[@ID=\'" + streamToModify + "\']";

        try {

            removeElements(FoxmlUtils.getElement(foxmlDocument, streamXPath), foxmlDocument, nextToLastStreamXPath);
        } catch (XPathExpressionException e) {
            LOGGER.warn("XPath failure", e);
        }

    }

    /**
     * @param detail
     */

    private static void modifyLabel(DigitalObjectDetail detail, Document foxmlDocument) {
        String propertyLabelXPath = "//foxml:objectProperties/foxml:property[@NAME=\'" + LABEL_VALUE + "\']";

        try {
            FoxmlUtils.getElement(foxmlDocument, propertyLabelXPath).setAttribute("VALUE", detail.getLabel());
        } catch (XPathExpressionException e) {
            LOGGER.warn("XPath failure", e);
        }
    }

    /**
     * Gets the related.
     * 
     * @param uuid
     *        the uuid
     * @return the related
     */
    public static ArrayList<ArrayList<String>> getRelated(final String uuid) {
        List<RelationshipTuple> triplets = FedoraUtils.getSubjectPids(uuid);
        if (triplets == null) { // RI can be disabled
            return null;
        }
        ArrayList<ArrayList<String>> returnList = new ArrayList<ArrayList<String>>(triplets.size());
        for (RelationshipTuple triplet : triplets) {
            //            System.err.println(triplet.toString());
            ArrayList<String> relatedRecord = new ArrayList<String>(2);
            String subject = triplet.getSubject().substring((Constants.FEDORA_INFO_PREFIX).length());
            String predicate = null;
            if (triplet.getPredicate().startsWith(Constants.FEDORA_INFO_PREFIX)) {
                predicate = triplet.getPredicate().substring(Constants.FEDORA_INFO_PREFIX.length());
            } else {
                predicate = triplet.getPredicate()
                        .substring(FedoraNamespaces.ONTOLOGY_RELATIONSHIP_NAMESPACE_URI.length());
            }

            relatedRecord.add(subject);
            relatedRecord.add(predicate);
            returnList.add(relatedRecord);
        }
        return returnList;
    }

    @SuppressWarnings("serial")
    public static ArrayList<ArrayList<String>> getAllChildren(String uuid) {
        List<RelationshipTuple> triplets = FedoraUtils.getObjectPids(uuid);
        ArrayList<ArrayList<String>> children = new ArrayList<ArrayList<String>>();

        if (triplets != null) {
            for (final RelationshipTuple triplet : triplets) {
                if (triplet.getObject().contains("uuid")
                        && triplet.getObject().contains(Constants.FEDORA_INFO_PREFIX)) {

                    final String childUuid = triplet.getObject().substring((Constants.FEDORA_INFO_PREFIX).length());

                    if (!childUuid.contains("/")) {
                        children.add(new ArrayList<String>() {

                            {
                                add(childUuid);
                                add(triplet.getPredicate());
                            }
                        });
                    }
                }
            }
        }
        return children;
    }

    public static boolean putRelsExt(String uuid, String content, boolean versionable) {
        String url = null;
        try {
            url = configuration.getFedoraHost() + "/objects/" + uuid + "/datastreams/RELS-EXT?versionable="
                    + versionable + java.net.URLEncoder.encode("&mimeType=application/rdf+xml", "UTF-8");
        } catch (UnsupportedEncodingException e) {
            LOGGER.warn("Encoding failure", e);
        }

        String usr = configuration.getFedoraLogin();
        String pass = configuration.getFedoraPassword();
        if (content != null) {
            RESTHelper.put(url, content, usr, pass, false);
            return true;
        }
        return false;
    }

    public static DigitalObjectModel getModel(String uuid) throws IOException {
        DigitalObjectModel model = null;
        try {
            model = fedoraAccess.getDigitalObjectModel(uuid);
        } catch (ConnectionException e) {
            LOGGER.error("Digital object " + uuid + " is not in the repository. " + e.getMessage());
            throw e;
        } catch (IOException e) {
            LOGGER.warn("Could not get model of object " + uuid + ". Using generic model handler.", e);
            throw e;
        }
        return model;
    }
}