diff --git a/mycore-base/src/main/java/org/mycore/common/xml/MCRURIResolver.java b/mycore-base/src/main/java/org/mycore/common/xml/MCRURIResolver.java index adcaac575d..8f842ced0e 100644 --- a/mycore-base/src/main/java/org/mycore/common/xml/MCRURIResolver.java +++ b/mycore-base/src/main/java/org/mycore/common/xml/MCRURIResolver.java @@ -1025,7 +1025,7 @@ public Source resolve(String href, String base) { } } catch (Exception ex) { LOGGER.info("MCRNotNullResolver caught exception: {}", ex.getLocalizedMessage()); - LOGGER.debug(ex.getStackTrace()); + LOGGER.debug(ex); LOGGER.debug("MCRNotNullResolver returning empty xml"); return new JDOMSource(new Element("null")); } diff --git a/mycore-mods/src/main/java/org/mycore/mods/merger/MCRAbstractRedundantModsEventHandler.java b/mycore-mods/src/main/java/org/mycore/mods/merger/MCRAbstractRedundantModsEventHandler.java new file mode 100644 index 0000000000..e5ffb6ccc2 --- /dev/null +++ b/mycore-mods/src/main/java/org/mycore/mods/merger/MCRAbstractRedundantModsEventHandler.java @@ -0,0 +1,169 @@ +package org.mycore.mods.merger; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.jdom2.Element; +import org.mycore.common.MCRConstants; +import org.mycore.common.events.MCREvent; +import org.mycore.common.events.MCREventHandlerBase; +import org.mycore.datamodel.classifications2.MCRCategoryID; +import org.mycore.datamodel.metadata.MCRObject; +import org.mycore.mods.MCRMODSWrapper; +import org.mycore.mods.classification.MCRClassMapper; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +/** + * Checks for and removes redundant classifications in Mods-Documents. If a classification category and + * the classification's child category are both present in the document, the parent classification will + * be removed.

+ * This abstract class can be extended by overriding the following abstract methods: + *
    + *
  1. {@link MCRAbstractRedundantModsEventHandler#isConsistent(Element, Element)}: custom checks for + * the two classifications' consistency.
  2. + *
  3. {@link MCRAbstractRedundantModsEventHandler#getClassificationElementName()}: the name of + * the mods-element that the EventHandler is checking duplicates for.
  4. + *
+ */ +public abstract class MCRAbstractRedundantModsEventHandler extends MCREventHandlerBase { + + private final Logger logger = LogManager.getLogger(getClass()); + + @Override + protected void handleObjectCreated(MCREvent evt, MCRObject obj) { + mergeCategories(obj); + } + + @Override + protected void handleObjectUpdated(MCREvent evt, MCRObject obj) { + mergeCategories(obj); + } + + @Override + protected void handleObjectRepaired(MCREvent evt, MCRObject obj) { + mergeCategories(obj); + } + + /** + * Merging classifications by detaching parent-categories inside an {@link MCRObject}. + * Mods-element is traversed for classifications, found relatedItems are processed separately from + * the rest of the document. + * @param obj the handled object + */ + protected void mergeCategories(MCRObject obj) { + MCRMODSWrapper mcrmodsWrapper = new MCRMODSWrapper(obj); + if (mcrmodsWrapper.getMODS() == null) { + return; + } + logger.info("merge redundant " + getClassificationElementName() + " categories for {}", obj.getId()); + + Element mods = mcrmodsWrapper.getMODS(); + List supportedElements = getAllDescendants(mods).stream() + .filter(element -> element.getName().equals(getClassificationElementName())) + .filter(element -> MCRClassMapper.getCategoryID(element) != null).toList(); + dropRedundantCategories(supportedElements); + + List relatedItems = getAllRelatedItems(mods); + for (Element relatedItem : relatedItems) { + if (relatedItem.getAttribute("href", MCRConstants.XLINK_NAMESPACE) == null) { + dropRedundantCategories(getAllDescendants(relatedItem)); + } + } + } + + /** + * Recursively writes all child-elements of a given element into a list and returns the list once completed. + * @param element the parent element for which all children should be listed + * @return a list with all child-elements + */ + protected static List getAllDescendants(Element element) { + List descendants = new ArrayList<>(); + + for (Element child : element.getChildren()) { + if (!child.getName().equals("relatedItem")) { + descendants.add(child); + descendants.addAll(getAllDescendants(child)); + } + } + return descendants; + } + + /** + * Returns all relatedItem-Elements from a mods-Element. Assumes that relatedItems are only used at top-level. + * @param mods The mods-Element to be searched for relatedItems + * @return a List of all Elements with the name "relatedItem" + */ + protected static List getAllRelatedItems(Element mods) { + return mods.getChildren().stream().filter(child -> "relatedItem".equals(child.getName())).toList(); + } + + /** + * Iterates through a list of classification elements and for each element pair checks if one of the element + * is a parent category of the other. Calls + * {@link MCRAbstractRedundantModsEventHandler#isConsistent(Element, Element)} and only detaches parent element + * if the method returns true. + * @param elements a list of classification elements that are all compared to each other in pairs + */ + protected void dropRedundantCategories(List elements) { + for (int i = 0; i < elements.size(); i++) { + for (int j = i + 1; j < elements.size(); j++) { + + Element element1 = elements.get(i); + Element element2 = elements.get(j); + Element parentElement = MCRCategoryMerger.getElementWithParentCategory(element1, element2); + if (parentElement != null && isConsistent(element1, element2)) { + parentElement.detach(); + } + } + } + } + + /** + * Parses the authority name from an element. + * @param element the element using an authority + * @return the String of the authority + */ + protected String getAuthority(Element element) { + return element.getAttributeValue("authorityURI") != null ? + element.getAttributeValue("authorityURI") : element.getAttributeValue("authority"); + } + + /** + * Compares two classification elements for the same authority + * @param el1 first element to be compared + * @param el2 second element to be compared + * @return true if both have the same authority, or if none of them has an authority + */ + protected boolean hasSameAuthority(Element el1, Element el2) { + return Objects.equals(el1.getAttributeValue("authorityURI"), + el2.getAttributeValue("authorityURI")) && + Objects.equals(el1.getAttributeValue("authority"), el2.getAttributeValue("authority")); + } + + /** + * Get the name of an element's classification or return value "unknown". + * @param element the element that contains the classification + * @return the name of the classification or the string "unknown" + */ + protected String getClassificationName(Element element) { + return Optional.ofNullable(MCRClassMapper.getCategoryID(element)).map(MCRCategoryID::toString) + .orElse("unknown"); + } + + /** + * Method can be overridden to implement custom checks to two categories' consistency regarding attributes. + * @param element1 the first element to be compared + * @param element2 the first element to be compared + * @return will always return true + */ + protected abstract boolean isConsistent(Element element1, Element element2); + + /** + * Returns the name of the classification element that the specific EventHandler is handling. + * @return name of the classification element + */ + protected abstract String getClassificationElementName(); +} diff --git a/mycore-mods/src/main/java/org/mycore/mods/merger/MCRCategoryMerger.java b/mycore-mods/src/main/java/org/mycore/mods/merger/MCRCategoryMerger.java index 03c6bac639..a08e6307b2 100644 --- a/mycore-mods/src/main/java/org/mycore/mods/merger/MCRCategoryMerger.java +++ b/mycore-mods/src/main/java/org/mycore/mods/merger/MCRCategoryMerger.java @@ -19,8 +19,11 @@ package org.mycore.mods.merger; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Optional; +import org.jdom2.Element; import org.mycore.common.config.MCRConfiguration2; import org.mycore.datamodel.classifications2.MCRCategory; import org.mycore.datamodel.classifications2.MCRCategoryDAO; @@ -88,12 +91,39 @@ static boolean oneIsDescendantOfTheOther(MCRCategoryID idThis, MCRCategoryID idO } private static List getAncestorsAndSelf(MCRCategoryID categoryID) { - List ancestorsAndSelf = new ArrayList<>(DAO.getParents(categoryID)); + List ancestorsAndSelf = new ArrayList<>(Optional.ofNullable(DAO.getParents(categoryID)).orElse( + Collections.emptyList())); ancestorsAndSelf.remove(DAO.getRootCategory(categoryID, 0)); ancestorsAndSelf.add(DAO.getCategory(categoryID, 0)); return ancestorsAndSelf; } + /** + * Compares two {@link Element Elements} that are assumed to be categories. + * If it is determined that one Element is a parent category of the other, return the parent, else return null. + * @param element1 first Element to compare + * @param element2 second Element to compare + * @return the parent Element or null + */ + public static Element getElementWithParentCategory(Element element1, Element element2) { + MCRCategoryID idThis = MCRClassMapper.getCategoryID(element1); + MCRCategoryID idOther = MCRClassMapper.getCategoryID(element2); + if (idThis == null || idOther == null) { + return null; + } + + final String p = CONFIG_PREFIX + idThis.getRootID(); + if (idThis.getRootID().equals(idOther.getRootID()) && !MCRConfiguration2.getBoolean(p).orElse(true)) { + return null; + } + + if (idThis.equals(idOther) || !oneIsDescendantOfTheOther(idThis, idOther)) { + return null; + } + + return getAncestorsAndSelf(idThis).containsAll(getAncestorsAndSelf(idOther)) ? element2 : element1; + } + @Override public void mergeFrom(MCRMerger other) { MCRCategoryMerger cmo = (MCRCategoryMerger) other; diff --git a/mycore-mods/src/main/java/org/mycore/mods/merger/MCRRedundantModsClassificationEventHandler.java b/mycore-mods/src/main/java/org/mycore/mods/merger/MCRRedundantModsClassificationEventHandler.java new file mode 100644 index 0000000000..f4f8348d25 --- /dev/null +++ b/mycore-mods/src/main/java/org/mycore/mods/merger/MCRRedundantModsClassificationEventHandler.java @@ -0,0 +1,63 @@ +package org.mycore.mods.merger; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.jdom2.Element; + +import java.util.Objects; + +/** + * Checks for and removes redundant classifications in Mods-Documents. If a classification category and + * the classification's child category are both present in the document, the parent classification will + * be removed. + */ +public class MCRRedundantModsClassificationEventHandler extends MCRAbstractRedundantModsEventHandler { + + private static final Logger LOGGER = LogManager.getLogger(MCRRedundantModsClassificationEventHandler.class); + + protected static final String CLASSIFICATION_ELEMENT_NAME = "classification"; + + @Override + protected String getClassificationElementName() { + return CLASSIFICATION_ELEMENT_NAME; + } + + /** + * Returns false if the authorities of the two classification elements are the same, but + * the displayLabels differ from each other. + * @param el1 the first element to be compared + * @param el2 the first element to be compared + * @return false if inconsistent + */ + @Override + protected boolean isConsistent(Element el1, Element el2) { + return !hasSameAuthority(el1, el2) || checkDisplayLabelConsistence(el1, el2); + } + + /** + * Checks if both elements have the same displayLabel. Logs a warning if not. + * @param el1 first element to check + * @param el2 second element to check + * @return true, if both elements have the same displayLabel (or both have none) + */ + private boolean checkDisplayLabelConsistence(Element el1, Element el2) { + final String displayLabel1 = el1.getAttributeValue("displayLabel"); + final String displayLabel2 = el2.getAttributeValue("displayLabel"); + + final String classificationName1 = getClassificationName(el1); + final String classificationName2 = getClassificationName(el2); + + if (!Objects.equals(displayLabel1, displayLabel2)) { + + String logMessage = """ + There are inconsistencies found between the classifications {} and {}. + They have the same authority "{}" but {} has the displayLabel "{}" and {} has the displayLabel "{}"."""; + + LOGGER.warn(logMessage, classificationName1, classificationName2, getAuthority(el1), + classificationName1, displayLabel1, classificationName2, displayLabel2); + return false; + } + return true; + } + +} diff --git a/mycore-mods/src/main/java/org/mycore/mods/merger/MCRRedundantModsGenreEventHandler.java b/mycore-mods/src/main/java/org/mycore/mods/merger/MCRRedundantModsGenreEventHandler.java new file mode 100644 index 0000000000..0d786b15f9 --- /dev/null +++ b/mycore-mods/src/main/java/org/mycore/mods/merger/MCRRedundantModsGenreEventHandler.java @@ -0,0 +1,90 @@ +package org.mycore.mods.merger; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.jdom2.Element; + +import java.util.Objects; + +/** + * Checks for and removes redundant genres in Mods-Documents. If a genre category and + * the genre's child category are both present in the document, the parent genre will + * be removed. + */ +public class MCRRedundantModsGenreEventHandler extends MCRAbstractRedundantModsEventHandler { + + private static final Logger LOGGER = LogManager.getLogger(MCRRedundantModsGenreEventHandler.class); + + protected static final String CLASSIFICATION_ELEMENT_NAME = "genre"; + + @Override + protected String getClassificationElementName() { + return CLASSIFICATION_ELEMENT_NAME; + } + + /** + * Returns false if the authorities of the two genre elements are the same, but + * the displayLabels or the types differ from each other. + * @param el1 the first element to be compared + * @param el2 the first element to be compared + * @return false if inconsistent + */ + @Override + protected boolean isConsistent(Element el1, Element el2) { + return !hasSameAuthority(el1, el2) || + (checkDisplayLabelConsistence(el1, el2) && checkTypeConsistence(el1, el2)); + } + + /** + * Checks if both elements have the same displayLabel. Logs a warning if not. + * @param el1 first element to check + * @param el2 second element to check + * @return true, if both elements have the same displayLabel (or both have none) + */ + private boolean checkDisplayLabelConsistence(Element el1, Element el2) { + final String displayLabel1 = el1.getAttributeValue("displayLabel"); + final String displayLabel2 = el2.getAttributeValue("displayLabel"); + + final String classificationName1 = getClassificationName(el1); + final String classificationName2 = getClassificationName(el2); + + if (!Objects.equals(displayLabel1, displayLabel2)) { + + String logMessage = """ + There are inconsistencies found between the classifications {} and {}. + They have the same authority "{}" but {} has the displayLabel "{}" and {} has the displayLabel "{}"."""; + + LOGGER.warn(logMessage, classificationName1, classificationName2, getAuthority(el1), + classificationName1, displayLabel1, classificationName2, displayLabel2); + return false; + } + return true; + } + + /** + * Checks if both elements have the same type. Logs a warning if not. + * @param el1 first element to check + * @param el2 second element to check + * @return true, if both elements have the same type (or both have none) + */ + private boolean checkTypeConsistence(Element el1, Element el2) { + final String type1 = el1.getAttributeValue("type"); + final String type2 = el2.getAttributeValue("type"); + + final String classificationName1 = getClassificationName(el1); + final String classificationName2 = getClassificationName(el2); + + if (!Objects.equals(type1, type2)) { + String logMessage = """ + There are inconsistencies found between the classifications {} and {}. + They have the same authority "{}" but {} has the type "{}" and {} has the type "{}"."""; + + LOGGER.warn(logMessage, classificationName1, classificationName2, getAuthority(el1), + classificationName1, type1, classificationName2, type2); + + return false; + } + return true; + } + +} diff --git a/mycore-mods/src/main/resources/components/mods/config/mycore.properties b/mycore-mods/src/main/resources/components/mods/config/mycore.properties index b95409807f..31b57f4691 100644 --- a/mycore-mods/src/main/resources/components/mods/config/mycore.properties +++ b/mycore-mods/src/main/resources/components/mods/config/mycore.properties @@ -1,6 +1,8 @@ MCR.Metadata.Type.mods=true MCR.Metadata.ShareAgent.mods=org.mycore.mods.MCRMODSMetadataShareAgent MCR.EventHandler.MCRObject.040.Class=org.mycore.mods.MCRMODSLinksEventHandler +# MCR.EventHandler.MCRObject.016a.Class=org.mycore.mods.merger.MCRRedundantModsClassificationEventHandler +# MCR.EventHandler.MCRObject.016b.Class=org.mycore.mods.merger.MCRRedundantModsGenreEventHandler MCR.MODS.NewObjectType=mods MCR.MODS.Types=mods diff --git a/mycore-mods/src/test/java/org/mycore/mods/merger/MCRRedundantModsClassificationEventHandlerTest.java b/mycore-mods/src/test/java/org/mycore/mods/merger/MCRRedundantModsClassificationEventHandlerTest.java new file mode 100644 index 0000000000..6e6ff0389b --- /dev/null +++ b/mycore-mods/src/test/java/org/mycore/mods/merger/MCRRedundantModsClassificationEventHandlerTest.java @@ -0,0 +1,147 @@ +package org.mycore.mods.merger; + +import static org.junit.Assert.assertEquals; +import static org.mycore.common.MCRConstants.MODS_NAMESPACE; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.jdom2.Document; +import org.jdom2.Element; +import org.jdom2.JDOMException; +import org.jdom2.input.SAXBuilder; +import org.junit.Test; +import org.mycore.common.MCRJPATestCase; +import org.mycore.common.content.MCRJDOMContent; +import org.mycore.datamodel.classifications2.MCRCategoryDAO; +import org.mycore.datamodel.classifications2.MCRCategoryDAOFactory; +import org.mycore.datamodel.classifications2.utils.MCRXMLTransformer; +import org.mycore.datamodel.metadata.MCRObject; +import org.mycore.mods.MCRMODSWrapper; +import org.mycore.resource.MCRResourceHelper; + +import java.io.IOException; +import java.util.List; + +public class MCRRedundantModsClassificationEventHandlerTest extends MCRJPATestCase { + + public static final String TEST_DIRECTORY = "MCRRedundantModsClassificationEventHandlerTest/"; + + private static final Logger LOGGER = LogManager.getLogger(); + + @Override + public void setUp() throws Exception { + super.setUp(); + + MCRCategoryDAO categoryDao = MCRCategoryDAOFactory.getInstance(); + categoryDao.addCategory(null, MCRXMLTransformer.getCategory(loadXml("sdnb.xml"))); + categoryDao.addCategory(null, MCRXMLTransformer.getCategory(loadXml("sdnb2.xml"))); + categoryDao.addCategory(null, MCRXMLTransformer.getCategory(loadXml("sdnb3.xml"))); + } + + private Element loadMods(String fileName) throws Exception { + + MCRObject object = new MCRObject(); + Document modsDocument = loadXml(fileName); + + MCRMODSWrapper modsWrapper = new MCRMODSWrapper(object); + modsWrapper.setMODS(modsDocument.getRootElement().detach()); + modsWrapper.setID("junit", 1); + + MCRRedundantModsClassificationEventHandler mergeEventHandler = new MCRRedundantModsClassificationEventHandler(); + mergeEventHandler.handleObjectCreated(null, object); + + LOGGER.info(new MCRJDOMContent(modsWrapper.getMODS()).asString()); + + return modsWrapper.getMODS(); + + } + + private static Document loadXml(String fileName) throws JDOMException, IOException { + return new SAXBuilder().build(MCRResourceHelper.getResourceAsStream(TEST_DIRECTORY + fileName)); + } + + @Test + public void redundantClassificationsWithSameAuthorityAndLabelAreRemoved() throws Exception { + Element mods = loadMods("modsClassificationSameAuthoritySameLabel.xml"); + List classifications = mods.getChildren("classification", MODS_NAMESPACE); + + assertEquals(2, classifications.size()); + assertEquals("foo:x-1-1", getLabelFromAttributeAndCategoryIdFromTextValue(classifications.get(0))); + assertEquals("foo:y", getLabelFromAttributeAndCategoryIdFromTextValue(classifications.get(1))); + } + + @Test + public void redundantClassificationsWithSameAuthorityAndDifferingLabelAreKept() throws Exception { + Element mods = loadMods("modsClassificationSameAuthorityDifferingLabel.xml"); + List classifications = mods.getChildren("classification", MODS_NAMESPACE); + + assertEquals(4, classifications.size()); + assertEquals("foo:x", getLabelFromAttributeAndCategoryIdFromTextValue(classifications.get(0))); + assertEquals("bar:x-1", getLabelFromAttributeAndCategoryIdFromTextValue(classifications.get(1))); + assertEquals("baz:x-1-1", getLabelFromAttributeAndCategoryIdFromTextValue(classifications.get(2))); + assertEquals("foo:y", getLabelFromAttributeAndCategoryIdFromTextValue(classifications.get(3))); + } + + @Test + public void redundantClassificationsWithDifferingAuthorityAreKept() throws Exception { + Element mods = loadMods("modsClassificationDifferingAuthority.xml"); + List classifications = mods.getChildren("classification", MODS_NAMESPACE); + + assertEquals(4, classifications.size()); + assertEquals("foo:x", getLabelFromAttributeAndCategoryIdFromTextValue(classifications.get(0))); + assertEquals("foo:x-1", getLabelFromAttributeAndCategoryIdFromTextValue(classifications.get(1))); + assertEquals("foo:x-1-1", getLabelFromAttributeAndCategoryIdFromTextValue(classifications.get(2))); + assertEquals("foo:y", getLabelFromAttributeAndCategoryIdFromTextValue(classifications.get(3))); + } + + @Test + public void redundantClassificationsInRelatedItemAreKept() throws Exception { + Element mods = loadMods("modsClassificationsInRelatedItem.xml"); + Element relatedItem = mods.getChildren("relatedItem", MODS_NAMESPACE).getFirst(); + List classifications = relatedItem.getChildren("classification", MODS_NAMESPACE); + + assertEquals(4, classifications.size()); + assertEquals("foo:x", getLabelFromAttributeAndCategoryIdFromTextValue(classifications.get(0))); + assertEquals("foo:x-1", getLabelFromAttributeAndCategoryIdFromTextValue(classifications.get(1))); + assertEquals("foo:x-1-1", getLabelFromAttributeAndCategoryIdFromTextValue(classifications.get(2))); + assertEquals("foo:y", getLabelFromAttributeAndCategoryIdFromTextValue(classifications.get(3))); + } + + @Test + public void redundantClassificationsInRelatedItemWithoutXlinkAreRemoved() throws Exception { + Element mods = loadMods("modsClassificationsInRelatedItemNoXlink.xml"); + Element relatedItem = mods.getChildren("relatedItem", MODS_NAMESPACE).getFirst(); + List classifications = relatedItem.getChildren("classification", MODS_NAMESPACE); + + assertEquals(2, classifications.size()); + assertEquals("foo:x-1-1", getLabelFromAttributeAndCategoryIdFromTextValue(classifications.get(0))); + assertEquals("foo:y", getLabelFromAttributeAndCategoryIdFromTextValue(classifications.get(1))); + } + + @Test + public void redundantClassificationsInModsAndRelatedItemNoInteraction() throws Exception { + Element mods = loadMods("modsClassificationsInAndOutsideRelatedItem.xml"); + List classificationsOutside = mods.getChildren("classification", MODS_NAMESPACE); + + assertEquals(1, classificationsOutside.size()); + assertEquals("foo:x-1", getLabelFromAttributeAndCategoryIdFromTextValue(classificationsOutside.getFirst())); + + List classificationsInside = mods.getChild("relatedItem", MODS_NAMESPACE) + .getChildren("classification", MODS_NAMESPACE); + + assertEquals(3, classificationsInside.size()); + assertEquals("foo:x-1-1", getLabelFromAttributeAndCategoryIdFromTextValue(classificationsInside.get(0))); + assertEquals("foo:x-1-1-1", getLabelFromAttributeAndCategoryIdFromTextValue(classificationsInside.get(1))); + assertEquals("foo:y", getLabelFromAttributeAndCategoryIdFromTextValue(classificationsInside.get(2))); + } + + private String getLabelFromAttributeAndCategoryIdFromTextValue(Element element) { + return element.getAttributeValue("displayLabel") + ":" + element.getText().trim(); + } + + private String getCategoryIdFromValueUri(Element element) { + String uri = element.getAttribute("valueURI").getValue(); + return uri.substring(uri.indexOf('#') + 1); + } + +} diff --git a/mycore-mods/src/test/java/org/mycore/mods/merger/MCRRedundantModsGenreEventHandlerTest.java b/mycore-mods/src/test/java/org/mycore/mods/merger/MCRRedundantModsGenreEventHandlerTest.java new file mode 100644 index 0000000000..35bf91428b --- /dev/null +++ b/mycore-mods/src/test/java/org/mycore/mods/merger/MCRRedundantModsGenreEventHandlerTest.java @@ -0,0 +1,171 @@ +package org.mycore.mods.merger; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.jdom2.Document; +import org.jdom2.Element; +import org.jdom2.JDOMException; +import org.jdom2.input.SAXBuilder; +import org.junit.Test; +import org.mycore.common.MCRJPATestCase; +import org.mycore.common.content.MCRJDOMContent; +import org.mycore.datamodel.classifications2.MCRCategoryDAO; +import org.mycore.datamodel.classifications2.MCRCategoryDAOFactory; +import org.mycore.datamodel.classifications2.utils.MCRXMLTransformer; +import org.mycore.datamodel.metadata.MCRObject; +import org.mycore.mods.MCRMODSWrapper; +import org.mycore.resource.MCRResourceHelper; + +import java.io.IOException; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.mycore.common.MCRConstants.MODS_NAMESPACE; + +public class MCRRedundantModsGenreEventHandlerTest extends MCRJPATestCase { + + public static final String TEST_DIRECTORY = "MCRRedundantModsGenreEventHandlerTest/"; + + private static final Logger LOGGER = LogManager.getLogger(); + + @Override + public void setUp() throws Exception { + super.setUp(); + + MCRCategoryDAO categoryDao = MCRCategoryDAOFactory.getInstance(); + categoryDao.addCategory(null, MCRXMLTransformer.getCategory(loadXml("genre.xml"))); + categoryDao.addCategory(null, MCRXMLTransformer.getCategory(loadXml("genre2.xml"))); + } + + private Element loadMods(String fileName) throws Exception { + + MCRObject object = new MCRObject(); + Document modsDocument = loadXml(fileName); + + MCRMODSWrapper modsWrapper = new MCRMODSWrapper(object); + modsWrapper.setMODS(modsDocument.getRootElement().detach()); + modsWrapper.setID("junit", 1); + + MCRRedundantModsGenreEventHandler mergeEventHandler = new MCRRedundantModsGenreEventHandler(); + mergeEventHandler.handleObjectCreated(null, object); + + LOGGER.info(new MCRJDOMContent(modsWrapper.getMODS()).asString()); + + return modsWrapper.getMODS(); + + } + + private static Document loadXml(String fileName) throws JDOMException, IOException { + return new SAXBuilder().build(MCRResourceHelper.getResourceAsStream(TEST_DIRECTORY + fileName)); + } + + @Test + public void redundantGenresAreRemovedAscending() throws Exception { + Element mods = loadMods("modsGenresAscending.xml"); + List genres = mods.getChildren("genre", MODS_NAMESPACE); + + assertEquals(2, genres.size()); + assertEquals("x-1-1", getCategoryIdFromValueUri(genres.get(0))); + assertEquals("y", getCategoryIdFromValueUri(genres.get(1))); + } + + @Test + public void redundantGenresAreRemovedDescending() throws Exception { + Element mods = loadMods("modsGenresDescending.xml"); + List genres = mods.getChildren("genre", MODS_NAMESPACE); + + assertEquals(2, genres.size()); + assertEquals("y", getCategoryIdFromValueUri(genres.get(0))); + assertEquals("x-1-1", getCategoryIdFromValueUri(genres.get(1))); + } + + @Test + public void redundantGenresInRelatedItemAreKept() throws Exception { + Element mods = loadMods("modsGenresInRelatedItem.xml"); + Element relatedItem = mods.getChildren("relatedItem", MODS_NAMESPACE).getFirst(); + List genres = relatedItem.getChildren("genre", MODS_NAMESPACE); + + assertEquals(4, genres.size()); + assertEquals("x", getCategoryIdFromValueUri(genres.get(0))); + assertEquals("x-1", getCategoryIdFromValueUri(genres.get(1))); + assertEquals("x-1-1", getCategoryIdFromValueUri(genres.get(2))); + assertEquals("y", getCategoryIdFromValueUri(genres.get(3))); + } + + @Test + public void redundantGenresInRelatedItemWithoutXlinkAreRemoved() throws Exception { + Element mods = loadMods("modsGenresInRelatedItemNoXlink.xml"); + Element relatedItem = mods.getChildren("relatedItem", MODS_NAMESPACE).getFirst(); + List genres = relatedItem.getChildren("genre", MODS_NAMESPACE); + + assertEquals(2, genres.size()); + assertEquals("x-1-1", getCategoryIdFromValueUri(genres.get(0))); + assertEquals("y", getCategoryIdFromValueUri(genres.get(1))); + } + + @Test + public void redundantGenresWithSameAuthorityAndDifferingLabelAreKept() throws Exception { + Element mods = loadMods("modsGenreSameAuthorityDifferingLabel.xml"); + List genres = mods.getChildren("genre", MODS_NAMESPACE); + + assertEquals(4, genres.size()); + assertEquals("foo:x", getLabelFromAttributeAndCategoryIdFromValueUri(genres.get(0))); + assertEquals("bar:x-1", getLabelFromAttributeAndCategoryIdFromValueUri(genres.get(1))); + assertEquals("baz:x-1-1", getLabelFromAttributeAndCategoryIdFromValueUri(genres.get(2))); + assertEquals("foo:y", getLabelFromAttributeAndCategoryIdFromValueUri(genres.get(3))); + } + + @Test + public void redundantGenresWithSameAuthorityAndDifferingTypeAreKept() throws Exception { + Element mods = loadMods("modsGenreSameAuthorityDifferingType.xml"); + List genres = mods.getChildren("genre", MODS_NAMESPACE); + + assertEquals(4, genres.size()); + assertEquals("foo:x", getTypeFromAttributeAndCategoryIdFromValueUri(genres.get(0))); + assertEquals("bar:x-1", getTypeFromAttributeAndCategoryIdFromValueUri(genres.get(1))); + assertEquals("baz:x-1-1", getTypeFromAttributeAndCategoryIdFromValueUri(genres.get(2))); + assertEquals("foo:y", getTypeFromAttributeAndCategoryIdFromValueUri(genres.get(3))); + } + + @Test + public void redundantGenresWithDifferingAuthorityAreKept() throws Exception { + Element mods = loadMods("modsGenreDifferingAuthority.xml"); + List genres = mods.getChildren("genre", MODS_NAMESPACE); + + assertEquals(3, genres.size()); + assertEquals("intern:y", getTypeFromAttributeAndCategoryIdFromValueUri(genres.get(0))); + assertEquals("intern:x-1-1", getTypeFromAttributeAndCategoryIdFromValueUri(genres.get(1))); + assertEquals("intern:x-1", getTypeFromAttributeAndCategoryIdFromValueUri(genres.get(2))); + } + + @Test + public void redundantGenresInModsAndRelatedItemNoInteraction() throws Exception { + Element mods = loadMods("modsGenresInAndOutsideRelatedItem.xml"); + List genresOutside = mods.getChildren("genre", MODS_NAMESPACE); + + + assertEquals(1, genresOutside.size()); + assertEquals("intern:x-1", getTypeFromAttributeAndCategoryIdFromValueUri(genresOutside.getFirst())); + + List genresInside = mods.getChild("relatedItem", MODS_NAMESPACE) + .getChildren("genre", MODS_NAMESPACE); + + assertEquals(2, genresInside.size()); + assertEquals("intern:x-1-1-1", getTypeFromAttributeAndCategoryIdFromValueUri(genresInside.get(0))); + assertEquals("intern:y", getTypeFromAttributeAndCategoryIdFromValueUri(genresInside.get(1))); + } + + private String getCategoryIdFromValueUri(Element element) { + String uri = element.getAttribute("valueURI").getValue(); + return uri.substring(uri.indexOf('#') + 1); + } + + private String getLabelFromAttributeAndCategoryIdFromValueUri(Element element) { + return element.getAttributeValue("displayLabel") + ":" + getCategoryIdFromValueUri(element); + } + + private String getTypeFromAttributeAndCategoryIdFromValueUri(Element element) { + return element.getAttributeValue("type") + ":" + getCategoryIdFromValueUri(element); + } + +} diff --git a/mycore-mods/src/test/resources/MCRRedundantModsClassificationEventHandlerTest/modsClassificationDifferingAuthority.xml b/mycore-mods/src/test/resources/MCRRedundantModsClassificationEventHandlerTest/modsClassificationDifferingAuthority.xml new file mode 100644 index 0000000000..ec469e0378 --- /dev/null +++ b/mycore-mods/src/test/resources/MCRRedundantModsClassificationEventHandlerTest/modsClassificationDifferingAuthority.xml @@ -0,0 +1,7 @@ + + + x + x-1 + x-1-1 + y + diff --git a/mycore-mods/src/test/resources/MCRRedundantModsClassificationEventHandlerTest/modsClassificationSameAuthorityDifferingLabel.xml b/mycore-mods/src/test/resources/MCRRedundantModsClassificationEventHandlerTest/modsClassificationSameAuthorityDifferingLabel.xml new file mode 100644 index 0000000000..3731a8bf18 --- /dev/null +++ b/mycore-mods/src/test/resources/MCRRedundantModsClassificationEventHandlerTest/modsClassificationSameAuthorityDifferingLabel.xml @@ -0,0 +1,7 @@ + + + x + x-1 + x-1-1 + y + diff --git a/mycore-mods/src/test/resources/MCRRedundantModsClassificationEventHandlerTest/modsClassificationSameAuthoritySameLabel.xml b/mycore-mods/src/test/resources/MCRRedundantModsClassificationEventHandlerTest/modsClassificationSameAuthoritySameLabel.xml new file mode 100644 index 0000000000..2b3bfb605e --- /dev/null +++ b/mycore-mods/src/test/resources/MCRRedundantModsClassificationEventHandlerTest/modsClassificationSameAuthoritySameLabel.xml @@ -0,0 +1,7 @@ + + + x + x-1 + x-1-1 + y + diff --git a/mycore-mods/src/test/resources/MCRRedundantModsClassificationEventHandlerTest/modsClassificationsInAndOutsideRelatedItem.xml b/mycore-mods/src/test/resources/MCRRedundantModsClassificationEventHandlerTest/modsClassificationsInAndOutsideRelatedItem.xml new file mode 100644 index 0000000000..39837132f2 --- /dev/null +++ b/mycore-mods/src/test/resources/MCRRedundantModsClassificationEventHandlerTest/modsClassificationsInAndOutsideRelatedItem.xml @@ -0,0 +1,10 @@ + + + x + x-1 + + x-1-1 + x-1-1-1 + y + + diff --git a/mycore-mods/src/test/resources/MCRRedundantModsClassificationEventHandlerTest/modsClassificationsInRelatedItem.xml b/mycore-mods/src/test/resources/MCRRedundantModsClassificationEventHandlerTest/modsClassificationsInRelatedItem.xml new file mode 100644 index 0000000000..f128a0226d --- /dev/null +++ b/mycore-mods/src/test/resources/MCRRedundantModsClassificationEventHandlerTest/modsClassificationsInRelatedItem.xml @@ -0,0 +1,9 @@ + + + + x + x-1 + x-1-1 + y + + diff --git a/mycore-mods/src/test/resources/MCRRedundantModsClassificationEventHandlerTest/modsClassificationsInRelatedItemNoXlink.xml b/mycore-mods/src/test/resources/MCRRedundantModsClassificationEventHandlerTest/modsClassificationsInRelatedItemNoXlink.xml new file mode 100644 index 0000000000..ed201e6e56 --- /dev/null +++ b/mycore-mods/src/test/resources/MCRRedundantModsClassificationEventHandlerTest/modsClassificationsInRelatedItemNoXlink.xml @@ -0,0 +1,9 @@ + + + + x + x-1 + x-1-1 + y + + diff --git a/mycore-mods/src/test/resources/MCRRedundantModsClassificationEventHandlerTest/sdnb.xml b/mycore-mods/src/test/resources/MCRRedundantModsClassificationEventHandlerTest/sdnb.xml new file mode 100644 index 0000000000..fd44f35901 --- /dev/null +++ b/mycore-mods/src/test/resources/MCRRedundantModsClassificationEventHandlerTest/sdnb.xml @@ -0,0 +1,16 @@ + + + diff --git a/mycore-mods/src/test/resources/MCRRedundantModsClassificationEventHandlerTest/sdnb2.xml b/mycore-mods/src/test/resources/MCRRedundantModsClassificationEventHandlerTest/sdnb2.xml new file mode 100644 index 0000000000..f1390452f1 --- /dev/null +++ b/mycore-mods/src/test/resources/MCRRedundantModsClassificationEventHandlerTest/sdnb2.xml @@ -0,0 +1,14 @@ + + + diff --git a/mycore-mods/src/test/resources/MCRRedundantModsClassificationEventHandlerTest/sdnb3.xml b/mycore-mods/src/test/resources/MCRRedundantModsClassificationEventHandlerTest/sdnb3.xml new file mode 100644 index 0000000000..f9a34dff83 --- /dev/null +++ b/mycore-mods/src/test/resources/MCRRedundantModsClassificationEventHandlerTest/sdnb3.xml @@ -0,0 +1,14 @@ + + + diff --git a/mycore-mods/src/test/resources/MCRRedundantModsGenreEventHandlerTest/genre.xml b/mycore-mods/src/test/resources/MCRRedundantModsGenreEventHandlerTest/genre.xml new file mode 100644 index 0000000000..1521c7d81a --- /dev/null +++ b/mycore-mods/src/test/resources/MCRRedundantModsGenreEventHandlerTest/genre.xml @@ -0,0 +1,15 @@ + + + diff --git a/mycore-mods/src/test/resources/MCRRedundantModsGenreEventHandlerTest/genre2.xml b/mycore-mods/src/test/resources/MCRRedundantModsGenreEventHandlerTest/genre2.xml new file mode 100644 index 0000000000..caff642253 --- /dev/null +++ b/mycore-mods/src/test/resources/MCRRedundantModsGenreEventHandlerTest/genre2.xml @@ -0,0 +1,13 @@ + + + diff --git a/mycore-mods/src/test/resources/MCRRedundantModsGenreEventHandlerTest/modsGenreDifferingAuthority.xml b/mycore-mods/src/test/resources/MCRRedundantModsGenreEventHandlerTest/modsGenreDifferingAuthority.xml new file mode 100644 index 0000000000..1096b1d8c8 --- /dev/null +++ b/mycore-mods/src/test/resources/MCRRedundantModsGenreEventHandlerTest/modsGenreDifferingAuthority.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/mycore-mods/src/test/resources/MCRRedundantModsGenreEventHandlerTest/modsGenreSameAuthorityDifferingLabel.xml b/mycore-mods/src/test/resources/MCRRedundantModsGenreEventHandlerTest/modsGenreSameAuthorityDifferingLabel.xml new file mode 100644 index 0000000000..010b53dbfe --- /dev/null +++ b/mycore-mods/src/test/resources/MCRRedundantModsGenreEventHandlerTest/modsGenreSameAuthorityDifferingLabel.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/mycore-mods/src/test/resources/MCRRedundantModsGenreEventHandlerTest/modsGenreSameAuthorityDifferingType.xml b/mycore-mods/src/test/resources/MCRRedundantModsGenreEventHandlerTest/modsGenreSameAuthorityDifferingType.xml new file mode 100644 index 0000000000..1c9068cc8b --- /dev/null +++ b/mycore-mods/src/test/resources/MCRRedundantModsGenreEventHandlerTest/modsGenreSameAuthorityDifferingType.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/mycore-mods/src/test/resources/MCRRedundantModsGenreEventHandlerTest/modsGenresAscending.xml b/mycore-mods/src/test/resources/MCRRedundantModsGenreEventHandlerTest/modsGenresAscending.xml new file mode 100644 index 0000000000..757d558ea9 --- /dev/null +++ b/mycore-mods/src/test/resources/MCRRedundantModsGenreEventHandlerTest/modsGenresAscending.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/mycore-mods/src/test/resources/MCRRedundantModsGenreEventHandlerTest/modsGenresDescending.xml b/mycore-mods/src/test/resources/MCRRedundantModsGenreEventHandlerTest/modsGenresDescending.xml new file mode 100644 index 0000000000..c5bc0041b0 --- /dev/null +++ b/mycore-mods/src/test/resources/MCRRedundantModsGenreEventHandlerTest/modsGenresDescending.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/mycore-mods/src/test/resources/MCRRedundantModsGenreEventHandlerTest/modsGenresInAndOutsideRelatedItem.xml b/mycore-mods/src/test/resources/MCRRedundantModsGenreEventHandlerTest/modsGenresInAndOutsideRelatedItem.xml new file mode 100644 index 0000000000..b252825783 --- /dev/null +++ b/mycore-mods/src/test/resources/MCRRedundantModsGenreEventHandlerTest/modsGenresInAndOutsideRelatedItem.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/mycore-mods/src/test/resources/MCRRedundantModsGenreEventHandlerTest/modsGenresInRelatedItem.xml b/mycore-mods/src/test/resources/MCRRedundantModsGenreEventHandlerTest/modsGenresInRelatedItem.xml new file mode 100644 index 0000000000..98b4fe9149 --- /dev/null +++ b/mycore-mods/src/test/resources/MCRRedundantModsGenreEventHandlerTest/modsGenresInRelatedItem.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/mycore-mods/src/test/resources/MCRRedundantModsGenreEventHandlerTest/modsGenresInRelatedItemNoXlink.xml b/mycore-mods/src/test/resources/MCRRedundantModsGenreEventHandlerTest/modsGenresInRelatedItemNoXlink.xml new file mode 100644 index 0000000000..1179600ed6 --- /dev/null +++ b/mycore-mods/src/test/resources/MCRRedundantModsGenreEventHandlerTest/modsGenresInRelatedItemNoXlink.xml @@ -0,0 +1,9 @@ + + + + + + + + +