/** * Copyright 2008 The University of North Carolina at Chapel Hill * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package edu.unc.lib.dl.xml; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.log4j.Logger; import org.jdom2.Document; import org.jdom2.Element; import org.jdom2.JDOMException; import org.jdom2.Namespace; import org.jdom2.xpath.XPath; import edu.unc.lib.dl.util.ContentModelHelper; public class FOXMLJDOMUtil { public static enum ObjectProperty { createdDate(JDOMNamespaceUtil.FEDORA_MODEL_NS, "createdDate"), label(JDOMNamespaceUtil.FEDORA_MODEL_NS, "label"), ownerId( JDOMNamespaceUtil.FEDORA_MODEL_NS, "ownerId"), state(JDOMNamespaceUtil.FEDORA_MODEL_NS, "state"); private URI uri; ObjectProperty(Namespace ns, String suffix) { try { this.uri = new URI(ns.getURI() + suffix); } catch (URISyntaxException e) { Error x = new ExceptionInInitializerError("Cannot initialize ContentModelHelper"); x.initCause(e); throw x; } } public URI getURI() { return this.uri; } @Override public String toString() { return this.uri.toString(); } } private static final String __foxmlXpathPrefix = "f"; private static XPath _fileLocatorXpath; private static final Namespace _foxmlXpathNamespace = Namespace.getNamespace(__foxmlXpathPrefix, JDOMNamespaceUtil.FOXML_NS.getURI()); private static XPath _getAllDatastreamsXpath; private static XPath _labelXPath; private static XPath _pidXPath; private static final String datastreamXmlContentXpath = "/f:digitalObject/f:datastream[@ID='%DSID%']/f:datastreamVersion/f:xmlContent"; private static final String fileLocatorXpath = "//f:contentLocation"; private static final String getAllDatastreamsXpath = "/f:digitalObject/f:datastream"; private static final String getDatastreamXpath = "/f:digitalObject/f:datastream[@ID='%DSID%']"; private static final String labelXpath = "/f:digitalObject/f:objectProperties/f:property[@NAME='info:fedora/fedora-system:def/model#label']/@VALUE"; private static Logger log = Logger.getLogger(FOXMLJDOMUtil.class); private static final String pidXpath = "/f:digitalObject/@PID"; static { try { _pidXPath = XPath.newInstance(pidXpath); _pidXPath.addNamespace(_foxmlXpathNamespace); _labelXPath = XPath.newInstance(labelXpath); _labelXPath.addNamespace(_foxmlXpathNamespace); _fileLocatorXpath = XPath.newInstance(fileLocatorXpath); _fileLocatorXpath.addNamespace(_foxmlXpathNamespace); _getAllDatastreamsXpath = XPath.newInstance(getAllDatastreamsXpath); _getAllDatastreamsXpath.addNamespace(_foxmlXpathNamespace); } catch (JDOMException e) { log.error("Bad Configuration for FOXMLJDOMUtil", e); throw new IllegalArgumentException("Bad Configuration for FOXMLJDOMUtil", e); } } public static List<Element> getAllDatastreams(Document foxml) { List<Element> result = null; try { result = extracted(_getAllDatastreamsXpath.selectNodes(foxml)); } catch (JDOMException e) { log.warn("JDOMException trying to setDatastreamXmlContent", e); } return result; } @SuppressWarnings("unchecked") private static List<Element> extracted(@SuppressWarnings("rawtypes") List selectNodes) { return selectNodes; } public static Element getDatastream(Document foxml, String id) { Element result = null; try { XPath _getDatastreamXpath = XPath.newInstance(getDatastreamXpath.replaceFirst("%DSID%", id)); _getDatastreamXpath.addNamespace(_foxmlXpathNamespace); Object o = _getDatastreamXpath.selectSingleNode(foxml); if (o != null && o instanceof Element) { result = (Element) o; } } catch (JDOMException e) { log.warn("JDOMException trying to setDatastreamXmlContent", e); } return result; } public static List<Element> getFileLocators(Document foxml) { List<Element> result = new ArrayList<Element>(); try { result = extracted(_fileLocatorXpath.selectNodes(foxml)); } catch (JDOMException e) { log.warn("JDOMException trying to get label property from foxml", e); } catch (ClassCastException e) { log.error("programmer error", e); throw new Error("programmer error", e); } return result; } public static String getLabel(Document foxml) { String result = null; try { result = _labelXPath.valueOf(foxml); } catch (JDOMException e) { log.warn("JDOMException trying to get label property from foxml", e); } if (result != null && "".equals(result.trim())) { result = null; } return result; } public static String getPID(Document foxml) { String result = null; try { result = _pidXPath.valueOf(foxml).trim(); } catch (JDOMException e) { log.warn("JDOMException trying to get path in collection from foxml", e); } if ("".equals(result)) { result = null; } return result; } public static Document makeFOXMLDocument(String pid) { Document result = new Document(); Element rootElement = new Element("digitalObject", JDOMNamespaceUtil.FOXML_NS); rootElement.addNamespaceDeclaration(JDOMNamespaceUtil.XSI_NS); rootElement.setAttribute("VERSION", "1.1"); rootElement.setAttribute("schemaLocation", JDOMNamespaceUtil.FOXML_NS.getURI() + " http://www.fedora.info/definitions/1/0/foxml1-1.xsd", JDOMNamespaceUtil.XSI_NS); if (pid != null) { rootElement.setAttribute("PID", pid); } rootElement.addContent(new Element("objectProperties", JDOMNamespaceUtil.FOXML_NS)); result.setRootElement(rootElement); return result; } /** * Creates a FOXML locator element. * * @param id * @param ctrlGroup * @param locator * @param mimeType * @param locatorType * @param label * @return */ // public static Element makeLocatorDatastream(String id, String ctrlGroup, String locator, String mimeType, // String locatorType, String label, boolean versionable) { // Element dcDS = new Element("datastream", JDOMNamespaceUtil.FOXML_NS); // dcDS.setAttribute("ID", id); // dcDS.setAttribute("CONTROL_GROUP", ctrlGroup); // dcDS.setAttribute("STATE", "A"); // dcDS.setAttribute("VERSIONABLE", versionable ? "true" : "false" ); // Element dcDSV = new Element("datastreamVersion", JDOMNamespaceUtil.FOXML_NS); // dcDS.addContent(dcDSV); // dcDSV.setAttribute("ID", id + ".0"); // dcDSV.setAttribute("MIMETYPE", mimeType); // dcDSV.setAttribute("LABEL", label); // Element contentDigest = new Element("contentDigest", JDOMNamespaceUtil.FOXML_NS); // contentDigest.setAttribute("TYPE", "MD5"); // contentDigest.setAttribute("DIGEST", "none"); // dcDSV.addContent(contentDigest); // Element contentLocation = new Element("contentLocation", JDOMNamespaceUtil.FOXML_NS); // contentLocation.setAttribute("REF", locator); // contentLocation.setAttribute("TYPE", locatorType); // dcDSV.addContent(contentLocation); // return dcDS; // } public static Element makeLocatorDatastream(String id, String ctrlGroup, String locator, String mimeType, String locatorType, String label, boolean versionable, String md5checksum) { Element dcDS = new Element("datastream", JDOMNamespaceUtil.FOXML_NS); dcDS.setAttribute("ID", id); dcDS.setAttribute("CONTROL_GROUP", ctrlGroup); dcDS.setAttribute("STATE", "A"); dcDS.setAttribute("VERSIONABLE", versionable ? "true" : "false"); Element dcDSV = new Element("datastreamVersion", JDOMNamespaceUtil.FOXML_NS); dcDS.addContent(dcDSV); dcDSV.setAttribute("ID", id + ".0"); if (mimeType != null) dcDSV.setAttribute("MIMETYPE", mimeType); if (label != null) dcDSV.setAttribute("LABEL", label); Element contentDigest = new Element("contentDigest", JDOMNamespaceUtil.FOXML_NS); contentDigest.setAttribute("TYPE", "MD5"); if (md5checksum != null) { contentDigest.setAttribute("DIGEST", md5checksum); } else { contentDigest.setAttribute("DIGEST", "none"); } dcDSV.addContent(contentDigest); Element contentLocation = new Element("contentLocation", JDOMNamespaceUtil.FOXML_NS); contentLocation.setAttribute("REF", locator); contentLocation.setAttribute("TYPE", locatorType); dcDSV.addContent(contentLocation); return dcDS; } public static Element makeInlineXMLDatastreamElement(String id, String label, Element xmlData, boolean versioned) { return makeInlineXMLDatastreamElement(id, label, id + "1.0", xmlData, versioned); } public static Element makeInlineXMLDatastreamElement(String id, String label, String versionId, Element xmlData, boolean versioned) { Element dcDS = new Element("datastream", JDOMNamespaceUtil.FOXML_NS); dcDS.setAttribute("ID", id); dcDS.setAttribute("CONTROL_GROUP", "X"); dcDS.setAttribute("STATE", "A"); dcDS.setAttribute("VERSIONABLE", versioned ? "true" : "false"); Element dcDSV = new Element("datastreamVersion", JDOMNamespaceUtil.FOXML_NS); dcDS.addContent(dcDSV); dcDSV.setAttribute("ID", versionId); dcDSV.setAttribute("MIMETYPE", "text/xml"); dcDSV.setAttribute("LABEL", label); Element contentDigest = new Element("contentDigest", JDOMNamespaceUtil.FOXML_NS); contentDigest.setAttribute("TYPE", "MD5"); contentDigest.setAttribute("DIGEST", "none"); dcDSV.addContent(contentDigest); Element xmlContent = new Element("xmlContent", JDOMNamespaceUtil.FOXML_NS); dcDSV.addContent(xmlContent); xmlContent.addContent(xmlData); return dcDS; } /** * Sets the content of the datastream, creating it if it doesn't exist. * * @param foxml * the FOXML Document * @param datastreamId * the ID of the datastream * @param label * the label for the datastreamVersion (ignored if datastream exists) * @param newContent * the new Element content of the datastream (within xmlContent) */ public static void setInlineXMLDatastreamContent(Document foxml, String datastreamId, String label, Element newContent, boolean versioned) { try { XPath _datastreamXmlContentXpath = XPath.newInstance(datastreamXmlContentXpath.replaceFirst("%DSID%", datastreamId)); _datastreamXmlContentXpath.addNamespace(_foxmlXpathNamespace); Object o = _datastreamXmlContentXpath.selectSingleNode(foxml); Element xmlContent = null; if (o == null) { // there's no datastream w/ID, so make one and // add it! Element dsContent = makeInlineXMLDatastreamElement(datastreamId, label, newContent, versioned); foxml.getRootElement().addContent(dsContent); } else if (o instanceof Element) { xmlContent = (Element) o; xmlContent.setContent(newContent); } } catch (JDOMException e) { log.warn("JDOMException trying to setDatastreamXmlContent", e); } } public static void setProperty(Document doc, ObjectProperty prop, String value) { Element props = doc.getRootElement().getChild("objectProperties", JDOMNamespaceUtil.FOXML_NS); if (props == null) { props = new Element("objectProperties", JDOMNamespaceUtil.FOXML_NS); doc.getRootElement().addContent(1, props); } else { Iterator<Element> childIt = props.getChildren().iterator(); while (childIt.hasNext()){ Element el = childIt.next(); if (prop.toString().equals(el.getAttributeValue("NAME"))){ childIt.remove(); } } } Element newProp = new Element("property", JDOMNamespaceUtil.FOXML_NS); newProp.setAttribute("NAME", prop.toString()); newProp.setAttribute("VALUE", value); props.addContent(newProp); return; } public static Element getDatastreamContent(ContentModelHelper.Datastream datastream, Document foxml) { return getDatastreamContent(datastream, foxml.getRootElement()); } /** * Returns the content of an internal datastream from the given foxml. If the datastream is versionable, then the * most recent version of the datastream is returned. * * @param datastream * @param foxml * @return */ public static Element getDatastreamContent(ContentModelHelper.Datastream datastream, Element foxml) { Element dsVersion; Element datastreamEl = JDOMQueryUtil.getChildByAttribute(foxml, "datastream", JDOMNamespaceUtil.FOXML_NS, "ID", datastream.getName()); if (datastreamEl == null) { return null; } if (datastream.isVersionable()) { dsVersion = FOXMLJDOMUtil.getMostRecentDatastreamVersion(datastreamEl.getChildren("datastreamVersion", JDOMNamespaceUtil.FOXML_NS)); } else { dsVersion = datastreamEl.getChild("datastreamVersion", JDOMNamespaceUtil.FOXML_NS); } return dsVersion.getChild("xmlContent", JDOMNamespaceUtil.FOXML_NS).getChildren().get(0); } /** * Returns a map of all of the most recent versions of datastreams listed in the given FOXML document * * @param foxml * @return */ public static Map<String, Element> getMostRecentDatastreamMap(Document foxml) { Map<String, Element> datastreams = new HashMap<String, Element>(); List<?> datastreamList = foxml.getRootElement().getChildren("datastream", JDOMNamespaceUtil.FOXML_NS); for (Object datastreamObject : datastreamList) { Element datastreamEl = (Element) datastreamObject; String datastreamName = datastreamEl.getAttributeValue("ID"); if (datastreamName != null) { ContentModelHelper.Datastream datastreamClass = ContentModelHelper.Datastream.getDatastream(datastreamName); if (datastreamClass == null) continue; Element dsVersion; if (datastreamClass.isVersionable()) { dsVersion = FOXMLJDOMUtil.getMostRecentDatastreamVersion(datastreamEl.getChildren("datastreamVersion", JDOMNamespaceUtil.FOXML_NS)); } else { dsVersion = datastreamEl.getChild("datastreamVersion", JDOMNamespaceUtil.FOXML_NS); } datastreams.put(datastreamName, dsVersion); } } return datastreams; } public static Element getMostRecentDatastream(ContentModelHelper.Datastream datastream, Document foxml) { return getMostRecentDatastream(datastream, foxml.getRootElement()); } public static Element getMostRecentDatastream(ContentModelHelper.Datastream datastream, Element foxml) { List<?> datastreamList = foxml.getChildren("datastream", JDOMNamespaceUtil.FOXML_NS); for (Object datastreamObject : datastreamList) { Element datastreamEl = (Element) datastreamObject; String datastreamName = datastreamEl.getAttributeValue("ID"); if (datastreamName != null && datastreamName.equals(datastream.getName())) { Element dsVersion; if (datastream.isVersionable()) { dsVersion = FOXMLJDOMUtil.getMostRecentDatastreamVersion(datastreamEl.getChildren("datastreamVersion", JDOMNamespaceUtil.FOXML_NS)); } else { dsVersion = datastreamEl.getChild("datastreamVersion", JDOMNamespaceUtil.FOXML_NS); } return dsVersion; } } return null; } /** * Returns the most recent version of a datastream from the given set of datastream elements * * @param elements * @return */ public static Element getMostRecentDatastreamVersion(List<?> elements) { if (elements == null || elements.size() == 0) return null; if (elements.size() == 1) return (Element) elements.get(0); String mostRecentDate = ""; Element mostRecent = null; for (Object datastreamVersionObj : elements) { Element datastreamVersion = (Element) datastreamVersionObj; String created = datastreamVersion.getAttributeValue("CREATED"); if (mostRecentDate.compareTo(created) < 0) { mostRecentDate = created; mostRecent = datastreamVersion; } } if (mostRecent != null) return mostRecent; return (Element) elements.get(0); } /** * Returns the object of the relationship specified from the provided RELS-EXT datastream. Only returns the first * value if the relation occurs multiple times * * @param relationName * @param relationNS * @param relsExt * @return */ public static String getRelationValue(String relationName, Namespace relationNS, Element relsExt) { Element relationEl = relsExt.getChild(relationName, relationNS); if (relationEl != null) { String value = relationEl.getAttributeValue("resource", JDOMNamespaceUtil.RDF_NS); if (value == null) value = relationEl.getText(); return value; } return null; } public static List<String> getRelationValues(String relationName, Namespace relationNS, Element relsExt) { List<?> relationEls = relsExt.getChildren(relationName, relationNS); if (relationEls != null) { List<String> values = new ArrayList<String>(relationEls.size()); for (Object relationObj : relationEls) { values.add(((Element) relationObj).getAttributeValue("resource", JDOMNamespaceUtil.RDF_NS)); } return values; } return null; } public static Element getObjectProperties(Document foxml) { // /foxml:digitalObject/foxml:objectProperties return foxml.getRootElement().getChild("objectProperties", JDOMNamespaceUtil.FOXML_NS); } public static Element getRelsExt(Document foxml) { Element relsExt = getDatastreamContent(ContentModelHelper.Datastream.RELS_EXT, foxml); if ("RDF".equals(relsExt.getName()) && JDOMNamespaceUtil.RDF_NS.equals(relsExt.getNamespace())) { return relsExt.getChildren().get(0); } return relsExt; } }