org.mycore.datamodel.metadata.MCRObjectUtils Maven / Gradle / Ivy
/*
* This file is part of *** M y C o R e ***
* See http://www.mycore.de/ for details.
*
* MyCoRe 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 3 of the License, or
* (at your option) any later version.
*
* MyCoRe 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 MyCoRe. If not, see .
*/
package org.mycore.datamodel.metadata;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.jdom2.Attribute;
import org.jdom2.Element;
import org.jdom2.filter.Filters;
import org.jdom2.xpath.XPathExpression;
import org.jdom2.xpath.XPathFactory;
import org.mycore.common.MCRConstants;
import org.mycore.common.MCRException;
import org.mycore.common.MCRPersistenceException;
import org.mycore.common.content.MCRContent;
import org.mycore.datamodel.classifications2.MCRCategoryID;
import org.mycore.datamodel.common.MCRLinkTableManager;
import org.mycore.datamodel.common.MCRXMLMetadataManager;
/**
* This class contains several helper methods for {@link MCRObject}.
*
* @author Matthias Eichner
*/
public abstract class MCRObjectUtils {
private static XPathExpression META_LINK_HREF;
private static XPathExpression META_CLASS;
static {
// META_LINK_HREF
String linkExp = "./mycoreobject/metadata/*[@class='MCRMetaLinkID']/*/@xlink:href";
META_LINK_HREF = XPathFactory.instance().compile(linkExp, Filters.attribute(), null,
MCRConstants.getStandardNamespaces());
// META_CLASS
String classExp = "./mycoreobject/metadata/*[@class='MCRMetaClassification']/*";
META_CLASS = XPathFactory.instance().compile(classExp, Filters.element(), null,
MCRConstants.getStandardNamespaces());
}
/**
* Retrieves a list of all ancestors of the given object. The first entry
* is the parent object, the last entry is the root node. Returns an empty
* list if no ancestor is found.
*
* @return list of ancestors
*/
public static List getAncestors(MCRObject mcrObject) {
List ancestorList = new ArrayList<>();
while (mcrObject.hasParent()) {
MCRObjectID parentID = mcrObject.getStructure().getParentID();
MCRObject parent = MCRMetadataManager.retrieveMCRObject(parentID);
ancestorList.add(parent);
mcrObject = parent;
}
return ancestorList;
}
/**
* Returns a list of all ancestors and the object itself. The first entry
* is the object itself, the last entry is the root node. Returns a list
* with one entry if no ancestor is found.
*
* @return list of ancestors
*/
public static List getAncestorsAndSelf(MCRObject mcrObject) {
List ancestorList = new ArrayList<>();
ancestorList.add(mcrObject);
ancestorList.addAll(getAncestors(mcrObject));
return ancestorList;
}
/**
* Returns the root ancestor of the given object. If the object has
* no parent null is returned.
*
* @param mcrObject object to get the root node
* @return root MCRObject
*/
public static MCRObject getRoot(MCRObject mcrObject) {
List ancestorList = getAncestors(mcrObject);
return ancestorList.isEmpty() ? null : ancestorList.get(ancestorList.size() - 1);
}
/**
* Returns all children of the given object. If the object has no
* children, an empty list is returned.
*
* @param mcrObject the mycore object
* @return list of all children
*/
public static List getChildren(MCRObject mcrObject) {
return mcrObject.getStructure()
.getChildren()
.stream()
.map(MCRMetaLinkID::getXLinkHrefID)
.map(MCRMetadataManager::retrieveMCRObject)
.collect(Collectors.toList());
}
/**
* Returns a list of all descendants and the object itself. For more information
* see {@link MCRObjectUtils#getDescendants}.
*
* @return list of all descendants and the object itself
*/
public static List getDescendantsAndSelf(MCRObject mcrObject) {
List objectList = getDescendants(mcrObject);
objectList.add(mcrObject);
return objectList;
}
/**
* Returns a list of all descendants of the given object. Be aware that
* there is no specific order. The list is empty if the object has no
* children.
*
* @return list of all descendants
*/
public static List getDescendants(MCRObject mcrObject) {
List objectList = new ArrayList<>();
getChildren(mcrObject).forEach(child -> objectList.addAll(getDescendantsAndSelf(child)));
return objectList;
}
/**
* Returns all derivates connected with this object. This includes derivates which are defined in the
* structure part and also derivate links.
*
* @param mcrObjectID object identifier to get the root node
* @return set of derivates
*/
public static List getDerivates(MCRObjectID mcrObjectID) {
MCRLinkTableManager linkTableManager = MCRLinkTableManager.instance();
Stream derivateStream = linkTableManager
.getDestinationOf(mcrObjectID, MCRLinkTableManager.ENTRY_TYPE_DERIVATE).stream();
Stream derivateLinkStream = linkTableManager
.getDestinationOf(mcrObjectID, MCRLinkTableManager.ENTRY_TYPE_DERIVATE_LINK).stream()
.map(link -> link.substring(0, link.indexOf("/")));
return Stream.concat(derivateStream, derivateLinkStream).distinct().map(MCRObjectID::getInstance)
.collect(Collectors.toList());
}
/**
* Returns a list of {@link MCRObject}s which are linked in the given object
* by an {@link MCRMetaLinkID}. This does not return any {@link MCRObjectStructure}
* links or any {@link MCRMetaDerivateLink}s.
*
* @param object the object where to get the entitylinks from
* @return a list of linked objects
* @throws MCRPersistenceException one of the linked objects does not exists
*/
public static List getLinkedObjects(MCRObject object) throws MCRPersistenceException {
Stream stream = META_LINK_HREF.evaluate(object.createXML()).stream().map(Attribute::getValue);
return stream.map(MCRObjectID::getInstance).peek(id -> {
if (!MCRMetadataManager.exists(id)) {
throw new MCRPersistenceException("MCRObject " + id + " is linked with (part of the metadata) "
+ object.getId() + " but does not exist.");
}
}).map(MCRMetadataManager::retrieveMCRObject).collect(Collectors.toList());
}
/**
* Removes all links of the source object. This includes parent links, children links and metadata links. A list
* of all updated objects is returned.
* Be aware that this method does not take care of storing the returned objects.
*
* @param sourceId id of the object
* @return a stream of updated objects where a link of the source was removed
*/
public static Stream removeLinks(MCRObjectID sourceId) {
return MCRLinkTableManager.instance().getSourceOf(sourceId).stream().filter(MCRObjectID::isValid)
.map(MCRObjectID::getInstance).map(MCRMetadataManager::retrieveMCRObject)
.flatMap(linkedObject -> MCRObjectUtils.removeLink(linkedObject, sourceId) ? Stream.of(linkedObject)
: Stream.empty());
}
/**
* Removes the linkToRemove in the metadata and the structure part of the source object. Be aware
* that this can lead to a zombie source object without a parent! Use this method with care!
*
* This method does not take care of storing the source object.
*
* @param source the source object where the links should be removed from
* @param linkToRemove the link id to remove
* @return true if a link was removed (the source object changed)
*/
public static boolean removeLink(MCRObject source, MCRObjectID linkToRemove) {
final AtomicBoolean updated = new AtomicBoolean(false);
// remove parent
if (source.getParent() != null && source.getParent().equals(linkToRemove)) {
source.getStructure().removeParent();
updated.set(true);
}
// remove children
if (source.getStructure().removeChild(linkToRemove)) {
updated.set(true);
}
// remove metadata parts
List emptyElements = source.getMetadata().stream()
.filter(metaElement -> metaElement.getClazz().equals(MCRMetaLinkID.class))
.flatMap(metaElement -> {
List linksToRemove = metaElement.stream().map(MCRMetaLinkID.class::cast)
.filter(metaLinkID -> metaLinkID.getXLinkHrefID().equals(linkToRemove))
.collect(Collectors.toList());
if (linksToRemove.size() > 0) {
updated.set(true);
linksToRemove.forEach(metaElement::removeMetaObject);
}
return metaElement.size() == 0 ? Stream.of(metaElement) : Stream.empty();
}).collect(Collectors.toList());
emptyElements.forEach(source.getMetadata()::removeMetadataElement);
return updated.get();
}
/**
* Returns a list of {@link MCRCategoryID}s which are used in the given object.
*
* @param object the object where to get the categories from
* @return a list of linked categories
*/
public static List getCategories(MCRObject object) {
Stream stream = META_CLASS.evaluate(object.createXML()).stream();
return stream.map((e) -> {
String classId = e.getAttributeValue("classid");
String categId = e.getAttributeValue("categid");
return new MCRCategoryID(classId, categId);
}).distinct().collect(Collectors.toList());
}
/**
* Restores a MyCoRe Object to the selected revision. Please note that children and derivates
* are not deleted or reverted!
*
* @param mcrId the mycore object identifier
* @param revision The revision to restore to. If this is lower than zero, the last revision is used.
* @return the new {@link MCRObject}
*
* @throws IOException An error occurred while retrieving the revision information. This is most
* likely due an svn error.
* @throws MCRPersistenceException There is no such object with the given id and revision.
* @throws ClassCastException The returning type must be the same as the type of the restored object
*/
public static T restore(MCRObjectID mcrId, Long revision) throws IOException,
MCRPersistenceException {
@SuppressWarnings("unchecked")
T mcrBase = (T) (mcrId.getTypeId().equals("derivate") ? new MCRDerivate() : new MCRObject());
// get content
MCRXMLMetadataManager xmlMetadataManager = MCRXMLMetadataManager.instance();
MCRContent content = xmlMetadataManager.retrieveContent(mcrId, revision);
if (content == null) {
throw new MCRPersistenceException("No such object " + mcrId + " with revision " + revision + ".");
}
// store it
try {
mcrBase.setFromJDOM(content.asXML());
if (MCRMetadataManager.exists(mcrId)) {
MCRMetadataManager.update(mcrBase);
} else {
if (mcrBase instanceof MCRObject) {
MCRMetadataManager.create((MCRObject) mcrBase);
} else {
MCRMetadataManager.create((MCRDerivate) mcrBase);
}
}
return mcrBase;
} catch (Exception exc) {
throw new MCRException("Unable to get object " + mcrId + " with revision " + revision + ".", exc);
}
}
}