/** * 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.util; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jdom2.Document; import org.jdom2.Element; import org.jdom2.JDOMException; import org.jdom2.filter.Filters; import edu.unc.lib.dl.fedora.PID; import edu.unc.lib.dl.xml.JDOMNamespaceUtil; /** * A set of functions that are useful for reading, interpreting and writing the container contents XML stream. * (MD_CONTENTS) * * @author count0 * */ public class ContainerContentsHelper { private static final Log log = LogFactory.getLog(ContainerContentsHelper.class); /** * Adds new children to the content index. If there is an order specified for a new child, then it will insert the * child at the specified position. Any existing children after the specified position will be shifted if neccessary. * * @param reordered * * @param oldContents * bytes for the old XML CONTENTS stream * @param topPid * new child pid * @param containerOrder * @return * @throws JDOMException * @throws IOException */ public static Document addChildContentAIPInCustomOrder(Document result, PID containerPID, Collection<ContainerPlacement> placements, List<PID> reordered) { log.debug("adding child content to MD_CONTENTS XML doc"); // first build a list of existing pid order in the container Element parentDiv = result.getRootElement().getChild("div", JDOMNamespaceUtil.METS_NS); List<Element> childDivs = parentDiv.getContent(Filters.element()); int maxExistingOrder = 5; if (childDivs.size() > 0) { maxExistingOrder = Integer.parseInt(childDivs.get(childDivs.size() - 1).getAttributeValue("ORDER")); } ArrayList<PID> order = new ArrayList<PID>(maxExistingOrder + 1); try { for (Element child : childDivs) { int ord = Integer.parseInt(child.getAttributeValue("ORDER")); PID pid = new PID(child.getAttributeValue("ID")); if (ord >= order.size()) { while (ord > order.size()) { // insert nulls order.add(null); } order.add(pid); } else { order.add(ord, pid); } } } catch (NullPointerException e) { throw new IllegalRepositoryStateException("Invalid container contents XML (MD_CONTENTS) on: ", e); } // FIXME: put all the old children in reordered pile, for now for (PID p : order) { if (p != null) { reordered.add(p); } } // clear out the current children parentDiv.removeContent(); // build a list of things with designated order and things with only sip // order List<ContainerPlacement> designatedOrder = new ArrayList<ContainerPlacement>(); List<ContainerPlacement> sipOrdered = new ArrayList<ContainerPlacement>(); List<ContainerPlacement> unordered = new ArrayList<ContainerPlacement>(); for (ContainerPlacement place : placements) { if (containerPID.equals(place.parentPID)) { // only place those objects that go in this container if (place.designatedOrder != null) { designatedOrder.add(place); } else if (place.sipOrder != null) { sipOrdered.add(place); } else { unordered.add(place); } } } // order.ensureCapacity(order.size() + designatedOrder.size() + // sipOrdered.size()); // sort designated ordered stuff by that order Comparator<ContainerPlacement> designatedSort = new Comparator<ContainerPlacement>() { @Override public int compare(ContainerPlacement o1, ContainerPlacement o2) { if (o1.designatedOrder > o2.designatedOrder) { return 1; } else if (o1.designatedOrder < o2.designatedOrder) { return -1; } return 0; } }; // ensure capacity for max of designated and existing, plus count of // sipOrdered and a buffer for collisions int maxDesignatedOrder = 0; if (designatedOrder.size() > 0) { Collections.sort(designatedOrder, designatedSort); maxDesignatedOrder = designatedOrder.get(designatedOrder.size() - 1).designatedOrder.intValue(); } int capacityEstimate = Math.max(maxDesignatedOrder, maxExistingOrder) + sipOrdered.size() + 10; order.ensureCapacity(capacityEstimate); // insert the objects with designated order for (ContainerPlacement place : designatedOrder) { int pos = place.designatedOrder.intValue(); if (pos >= order.size()) { while (pos > order.size()) { // index out of bounds, insert nulls order.add(null); } order.add(place.pid); } else { order.add(pos, place.pid); } } // append the objects with sip sibling order // sort sip ordered stuff by that order Comparator<ContainerPlacement> sipSort = new Comparator<ContainerPlacement>() { @Override public int compare(ContainerPlacement o1, ContainerPlacement o2) { if (o2.sipOrder == null || o1.sipOrder > o2.sipOrder) { return 1; } else if (o1.sipOrder == null || o1.sipOrder < o2.sipOrder) { return -1; } return 0; } }; Collections.sort(sipOrdered, sipSort); // add SIP ordered for (ContainerPlacement place : sipOrdered) { order.add(place.pid); } // add unordered for (ContainerPlacement place : unordered) { order.add(place.pid); } for (ListIterator<PID> li = order.listIterator(); li.hasNext();) { int ord = li.nextIndex(); PID pid = li.next(); if (pid != null) { Element el = new Element("div", parentDiv.getNamespace()).setAttribute("ID", pid.getPid()).setAttribute( "ORDER", String.valueOf(ord)); parentDiv.addContent(el); } } return result; } public static Document remove(Document oldXML, PID pid) { Element parentDiv = oldXML.getRootElement().getChild("div", JDOMNamespaceUtil.METS_NS); List<Element> childDivs = parentDiv.getChildren(); Element remove = null; for (Element child : childDivs) { String p = child.getAttributeValue("ID"); if (pid.getPid().equals(p)) { remove = child; break; } } parentDiv.removeContent(remove); return oldXML; } public static Document remove(Document oldXML, Collection<PID> children) { Element parentDiv = oldXML.getRootElement().getChild("div", JDOMNamespaceUtil.METS_NS); List<Element> childDivs = parentDiv.getChildren(); Iterator<Element> childIt = childDivs.iterator(); while (childIt.hasNext()) { Element child = childIt.next(); PID childPID = new PID(child.getAttributeValue("ID")); if (children.contains(childPID)) { childIt.remove(); } } return oldXML; } /** * @param oldXML * @param container * @param children * @return */ public static Document addChildContentListInCustomOrder(Document oldXML, PID container, List<PID> children, Collection<PID> reorderedPids) { log.debug("HERE incoming children:"); for (int i = 0; i < children.size(); i++) { log.debug(i + " => " + children.get(i)); } // first build a list of existing pid order in the container Element parentDiv = oldXML.getRootElement().getChild("div", JDOMNamespaceUtil.METS_NS); List<Element> childDivs = parentDiv.getChildren(); int maxExistingOrder = 5; if (childDivs.size() > 0) { maxExistingOrder = Integer.parseInt(childDivs.get(childDivs.size() - 1).getAttributeValue("ORDER")); } ArrayList<PID> order = new ArrayList<PID>(maxExistingOrder); try { for (Element child : childDivs) { int ord = Integer.parseInt(child.getAttributeValue("ORDER")); PID pid = new PID(child.getAttributeValue("ID")); if (ord >= order.size()) { while (ord > order.size()) { // insert nulls order.add(null); } order.add(pid); } else { order.add(ord, pid); } } } catch (NullPointerException e) { throw new IllegalRepositoryStateException("Invalid container contents XML (MD_CONTENTS) on: ", e); } log.debug("HERE order before merge:"); for (int i = 0; i < order.size(); i++) { log.debug(i + " => " + order.get(i)); } PID[] originalOrder = order.toArray(new PID[0]); // clear out the current children parentDiv.removeContent(); int maxIncomingOrder = 0; if (children.size() > 0) { maxIncomingOrder = children.size() - 1; } int capacityEstimate = Math.max(maxIncomingOrder, maxExistingOrder) + 10; order.ensureCapacity(capacityEstimate); for (ListIterator<PID> foo = children.listIterator(); foo.hasNext();) { int ord = foo.nextIndex(); PID child = foo.next(); if (ord >= order.size()) { while (ord > order.size()) { // insert nulls order.add(null); } order.add(child); } else { order.add(ord, child); } } if (log.isDebugEnabled()) { log.debug("HERE order after merge:"); for (int i = 0; i < order.size(); i++) { log.debug(i + " => " + order.get(i)); } } // Record changes to order if desired if (reorderedPids != null) { for (int i = 0; i < originalOrder.length; i++) { PID orig = originalOrder[i]; if (orig != null) { if (!orig.equals(order.get(i))) { reorderedPids.add(orig); } } } } for (ListIterator<PID> li = order.listIterator(); li.hasNext();) { int ord = li.nextIndex(); PID pid = li.next(); if (pid != null) { Element el = new Element("div", parentDiv.getNamespace()).setAttribute("ID", pid.getPid()).setAttribute( "ORDER", String.valueOf(ord)); parentDiv.addContent(el); } } return oldXML; } }