All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.mycore.datamodel.metadata.MCRObjectStructure Maven / Gradle / Ivy

There is a newer version: 2024.05
Show newest version
/*
 * 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.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jdom2.Element;
import org.mycore.common.MCRException;

import com.google.gson.JsonArray;
import com.google.gson.JsonObject;

/**
 * This class implements code for the inheritance of metadata of linked objects
 * and the linking of derivates onto an MCRObject. These links are described by
 * the MCRMetaLink class. For links to another object, there are
 * "locators" in use only, and the href variable gives the ID of the linked
 * object, while the label and title attributes can be used freely. Subtag name = "
 * <child>" means a child link from a "parent" object (collected in the
 * "children" and "parents" section of the "structure" part, respectively). The
 * child inherits all heritable metadata of the parent. If the parent itself is
 * a child of another parent, the heritable metadata of this "grand parent" is
 * inherited by the child as well. This mechanism recursively traces the full
 * inheritance hierarchy. So if the grand parent itself has a parent, this grand
 * parent parent's heritable metadata will be inherited and so on. Note, that it
 * is impossible to inherit metadata from multiple parents. In cases of multiple
 * inheritance request, an exception is thrown. A child link cannot occur twice
 * from the same object to the same href (preventing from doubled links). Not
 * supported by this class are links from or to a defined place of a document
 * (inner structure and combination of inner and outer structures of the
 * objects). This will possibly be done in a later extension of
 * MCRMetaLink and MCRObjectStructure.
 * 
 * @author Mathias Hegner
 * @author Jens Kupferschmidt
 * @version $Revision$ $Date: 2008-02-06 18:27:24 +0100 (Mi, 06 Feb
 *          2008) $
 */
public class MCRObjectStructure {

    private MCRMetaLinkID parent;

    private final ArrayList children;

    private final ArrayList derivates;

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

    /**
     * The constructor initializes NL (non-static, in order to enable different
     * NL's for different objects) and the link vectors the elements of which
     * are MCRMetaLink's.
     */
    public MCRObjectStructure() {
        children = new ArrayList<>();
        derivates = new ArrayList<>();
    }

    /**
     * This method clean the data lists parent, children and derivates of this
     * class.
     */
    public final void clear() {
        parent = null;
        children.clear();
        derivates.clear();
    }

    /**
     * This method clean the data lists children of this class.
     */
    public final void clearChildren() {
        children.clear();
    }

    /**
     * This method clean the data lists derivate of this class.
     */
    public final void clearDerivates() {
        derivates.clear();
    }

    /**
     * The method returns the parent link.
     * 
     * @return MCRMetaLinkID the corresponding link
     */
    public final MCRMetaLinkID getParent() {
        return parent;
    }

    /**
     * The method return the parent reference as a MCRObjectID.
     * 
     * @return the parent MCRObjectID or null if there is no parent present
     */
    public final MCRObjectID getParentID() {
        if (parent == null) {
            return null;
        }
        return parent.getXLinkHrefID();
    }

    /**
     * This method set the parent value from a given MCRMetaLinkID.
     * 
     * @param parent
     *            the MCRMetaLinkID to set
     *  
     */
    public final void setParent(MCRMetaLinkID parent) {
        this.parent = parent;
    }

    public final void setParent(MCRObjectID parentID) {
        setParent(parentID.toString());
    }

    public final void setParent(String parentID) {
        parent = new MCRMetaLinkID();
        parent.setSubTag("parent");
        parent.setReference(parentID, null, null);
    }

    /**
     * Removes the parent reference. Use this method with care!
     */
    public final void removeParent() {
        parent = null;
    }

    /**
     * The method appends a child ID to the child link list if and only if it is
     * not already contained in the list, preventing from doubly-linked objects.
     * If the link could be added a "true" will be returned, otherwise "false".
     * 
     * @param child
     *            the MCRMetaLinkID of the child
     * @return boolean true, if successfully done
     */
    public final boolean addChild(MCRMetaLinkID child) {
        for (MCRMetaLinkID c : children) {
            if (c.getXLinkHrefID().equals(child.getXLinkHrefID())) {
                return false;
            }
        }
        children.add(child);

        return true;
    }

    /**
     * removes a child link to another object.
     *  If the link was found a "true" will be returned, otherwise
     * "false".
     * 
     * @param href
     *            the MCRObjectID of the child
     * @return boolean true, if successfully completed
     */
    public final boolean removeChild(MCRObjectID href) {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Remove child ID {}", href);
        }
        return removeMetaLink(getChildren(), href);
    }

    /**
     * Checks if the child is in the children vector.
     *
     * @param childId child to check
     */
    public final boolean containsChild(MCRObjectID childId) {
        return getChildren().stream().map(MCRMetaLinkID::getXLinkHrefID).anyMatch(childId::equals);
    }

    /**
     * removes a derivate link.
     * If the link was found a "true" will be returned, otherwise
     * "false".
     * 
     * @param href
     *            the MCRObjectID of the child
     * @return boolean true, if successfully completed
     */
    public final boolean removeDerivate(MCRObjectID href) {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Remove derivate ID {}", href);
        }
        return removeMetaLink(getDerivates(), href);
    }

    /**
     * Removes a MCRMetaLinkID instance by it MCRObjectID.
     * @param list
     * @param href
     * @return
     */
    private boolean removeMetaLink(List list, MCRObjectID href) {
        final List toRemove = list.stream()
            .filter(ml -> ml.getXLinkHrefID().equals(href))
            .collect(Collectors.toList());
        return list.removeAll(toRemove);
    }

    /**
     * Returns all children in this structure
     * */
    public final List getChildren() {
        return children;
    }

    /**
     * addDerivate methode append the given derivate link data to the
     * derivate vector. If the link could be added a "true" will be returned,
     * otherwise "false".
     * 
     * @param derivate
     *            the link to be added as MCRMetaLinkID
     */
    public final boolean addDerivate(MCRMetaEnrichedLinkID derivate) {
        MCRObjectID href = derivate.getXLinkHrefID();
        if (containsDerivate(href)) {
            return false;
        }
        if (!MCRMetadataManager.exists(href)) {
            LOGGER.warn("Cannot find derivate {}, will add it anyway.", href);
        }
        derivates.add(derivate);
        derivates.sort(Comparator.comparingInt(MCRMetaEnrichedLinkID::getOrder));
        return true;
    }

    /**
     * Adds or updates the derivate link. Returns true if the derivate is added
     * or updated. Returns false when nothing is done.
     * 
     * @param derivateLink the link to add or update
     * @return true when the structure is changed
     */
    public final boolean addOrUpdateDerivate(MCRMetaEnrichedLinkID derivateLink) {
        if (derivateLink == null) {
            return false;
        }
        MCRObjectID derivateId = derivateLink.getXLinkHrefID();
        MCRMetaLinkID oldLink = getDerivateLink(derivateId);
        if (derivateLink.equals(oldLink)) {
            return false;
        }
        if (oldLink != null) {
            removeDerivate(oldLink.getXLinkHrefID());
        }
        return addDerivate(derivateLink);
    }

    /**
     * Checks if the derivate is in the derivate vector.
     * 
     * @param derivateId derivate to check
     */
    public final boolean containsDerivate(MCRObjectID derivateId) {
        return getDerivateLink(derivateId) != null;
    }

    /**
     * Returns the derivate link by id or null.
     */
    public final MCRMetaEnrichedLinkID getDerivateLink(MCRObjectID derivateId) {
        return getDerivates().stream()
            .filter(derivate -> derivate.getXLinkHrefID().equals(derivateId))
            .findAny()
            .orElse(null);
    }

    /** 
     * @return a list with all related derivate ids encapsulated within a {@link MCRMetaLinkID}
     * */
    public List getDerivates() {
        return this.derivates;
    }

    /**
     * While the preceding methods dealt with the structure's copy in memory
     * only, the following three will affect the operations to or from datastore
     * too. Thereby setFromDOM will read the structure data from an
     * XML input stream (the "structure" entry).
     * 
     * @param element
     *            the structure node list
     */
    public final void setFromDOM(Element element) {
        clear();
        Element subElement = element.getChild("children");

        if (subElement != null) {
            List childList = subElement.getChildren();

            for (Element linkElement : childList) {
                MCRMetaLinkID link = new MCRMetaLinkID();
                link.setFromDOM(linkElement);
                children.add(link);
            }
        }

        // Stricture parent part
        subElement = element.getChild("parents");

        if (subElement != null) {
            parent = new MCRMetaLinkID();
            parent.setFromDOM(subElement.getChild("parent"));
        }

        // Structure derivate part
        subElement = element.getChild("derobjects");

        if (subElement != null) {
            List derobjectList = subElement.getChildren();

            for (Element derElement : derobjectList) {
                addDerivate(MCRMetaEnrichedLinkID.fromDom(derElement));
            }
        }
    }

    /**
     * createXML is the inverse of setFromDOM and converts the
     * structure's memory copy into XML.
     * 
     * @exception MCRException
     *                if the content of this class is not valid
     * @return the structure XML
     */
    public final Element createXML() throws MCRException {
        try {
            validate();
        } catch (MCRException exc) {
            throw new MCRException("The content is not valid.", exc);
        }

        Element elm = new Element("structure");

        if (parent != null) {
            Element elmm = new Element("parents");
            elmm.setAttribute("class", "MCRMetaLinkID");
            elmm.addContent(parent.createXML());
            elm.addContent(elmm);
        }

        if (children.size() > 0) {
            Element elmm = new Element("children");
            elmm.setAttribute("class", "MCRMetaLinkID");
            for (MCRMetaLinkID child : getChildren()) {
                elmm.addContent(child.createXML());
            }
            elm.addContent(elmm);
        }

        if (derivates.size() > 0) {
            Element elmm = new Element("derobjects");
            elmm.setAttribute("class", "MCRMetaEnrichedLinkID");
            for (MCRMetaLinkID derivate : getDerivates()) {
                elmm.addContent(derivate.createXML());
            }
            elm.addContent(elmm);
        }

        return elm;
    }

    /**
     * Creates the JSON representation of this structure.
     * 
     * 
     *   {
     *     parent: {@link MCRMetaLinkID#createJSON()},
     *     children: [
     *      {@link MCRMetaLinkID#createJSON()}
     *      ...
     *     ],
     *     derivates: [
     *       {@link MCRMetaLinkID#createJSON()}
     *        ...
     *     ]
     *   }
     * 
* * @return a json gson representation of this structure */ public JsonObject createJSON() { JsonObject structure = new JsonObject(); // parent Optional.ofNullable(getParent()).ifPresent(link -> structure.add("parent", link.createJSON())); // children JsonArray children = new JsonArray(); getChildren().forEach(child -> children.add(child.createJSON())); structure.add("children", children); // derivates JsonArray derivates = new JsonArray(); getDerivates().forEach(derivate -> derivates.add(derivate.createJSON())); structure.add("derivates", derivates); return structure; } /** * The method print all informations about this MCRObjectStructure. */ public final void debug() { if (LOGGER.isDebugEnabled()) { for (MCRMetaLinkID linkID : derivates) { linkID.debug(); } if (parent != null) { parent.debug(); } for (MCRMetaLinkID linkID : children) { linkID.debug(); } } } /** * isValid checks whether all of the MCRMetaLink's in the link * vectors are valid or not. * * @return boolean true, if structure is valid */ public final boolean isValid() { try { validate(); return true; } catch (MCRException exc) { LOGGER.warn("The part of a is invalid.", exc); } return false; } /** * Validates this MCRObjectStructure. This method throws an exception if: *
    *
  • the parent is not null but invalid
  • *
  • one of the children is invalid
  • *
  • one of the derivates is invalid
  • *
* * @throws MCRException the MCRObjectStructure is invalid */ public void validate() throws MCRException { for (MCRMetaLinkID child : getChildren()) { try { child.validate(); } catch (Exception exc) { throw new MCRException("The link to the children '" + child.getXLinkHref() + "' is invalid.", exc); } } if (parent != null) { try { parent.validate(); } catch (Exception exc) { throw new MCRException("The link to the parent '" + parent.getXLinkHref() + "' is invalid.", exc); } } for (MCRMetaLinkID derivate : getDerivates()) { try { derivate.validate(); } catch (Exception exc) { throw new MCRException("The link to the derivate '" + derivate.getXLinkHref() + "' is invalid.", exc); } if (!derivate.getXLinkType().equals("locator")) { throw new MCRException("The xlink:type of the derivate link '" + derivate.getXLinkHref() + "' has to be 'locator' and not '" + derivate.getXLinkType() + "'."); } String typeId = derivate.getXLinkHrefID().getTypeId(); if (!typeId.equals("derivate")) { throw new MCRException("The derivate link '" + derivate.getXLinkHref() + "' is invalid. The _type_ has to be 'derivate' and not '" + typeId + "'."); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy