You can define a label x-mapping in a classification with space seperated categoryIds
* to which the classification will be mapped. You can further define a label x-mapping-xpath in a
- * classification which creates a classification mapping when the xPath inside the x-mapping-xpath attribute
- * is matched.
+ * classification which creates a classification mapping when the XPath inside the x-mapping-xpath attribute
+ * is matched. You can also define a label x-mapping-xpathfb which contains a fallback value
+ * in case that no other XPaths inside a classification match.
*
* <category ID="article" counter="1">
* <label xml:lang="en" text="Article / Chapter" />
* <label xml:lang="de" text="Artikel / Aufsatz" />
* <label xml:lang="x-mapping" text="diniPublType:article" />
* <label xml:lang="x-mapping-xpath" text="mods:genre[text()='article']" />
+ * <label xml:lang="x-mapping-xpathfb" text="mods:genre[text()='other']" />
* </category>
*
*
@@ -68,6 +71,8 @@ public class MCRClassificationMappingEventHandler extends MCREventHandlerBase {
public static final String LABEL_LANG_XPATH_MAPPING = "x-mapping-xpath";
+ public static final String LABEL_LANG_XPATH_MAPPING_FALLBACK = "x-mapping-xpathfb";
+
public static final String LABEL_LANG_X_MAPPING = "x-mapping";
public static final String XPATH_GENERATOR_NAME = "xpathmapping";
@@ -104,16 +109,22 @@ private static List> getXMappings(MCRCat
/**
* Searches all configured classifications
* (see {@link MCRClassificationMappingEventHandler#X_PATH_MAPPING_CLASSIFICATIONS}) for categories
- * with language label {@link MCRClassificationMappingEventHandler#LABEL_LANG_XPATH_MAPPING}.
- * All categories with said label and present in database are returned in a list.
- * @return a list of {@link MCRCategory categories} with the XPath-Mapping label
+ * with language labels {@link MCRClassificationMappingEventHandler#LABEL_LANG_XPATH_MAPPING} or
+ * {@link MCRClassificationMappingEventHandler#LABEL_LANG_XPATH_MAPPING_FALLBACK}.
+ * All categories with said label and present in database are returned in a Map of Sets,
+ * separated by their classification.
+ * @return a Map with classification-IDs as key and Sets of {@link MCRCategory categories}
+ * with the XPath-Mapping label as value
*/
- private static List loadAllXPathMappings() {
+ private static Map> loadAllXPathMappings() {
final MCRCategoryDAO dao = MCRCategoryDAOFactory.getInstance();
return Arrays.stream(X_PATH_MAPPING_CLASSIFICATIONS.trim().split(","))
- .flatMap(
- relevantClass -> dao.getCategoriesByClassAndLang(relevantClass, LABEL_LANG_XPATH_MAPPING).stream())
- .collect(Collectors.toList());
+ .collect(Collectors.toMap(
+ relevantClass -> relevantClass,
+ relevantClass -> Stream.concat(
+ dao.getCategoriesByClassAndLang(relevantClass, LABEL_LANG_XPATH_MAPPING).stream(),
+ dao.getCategoriesByClassAndLang(relevantClass, LABEL_LANG_XPATH_MAPPING_FALLBACK).stream())
+ .collect(Collectors.toSet())));
}
@Override
@@ -173,21 +184,47 @@ private void createMapping(MCRObject obj) {
mappedClassification.setAttribute("generator", generator);
MCRClassMapper.assignCategory(mappedClassification, mapping.getValue());
});
- // check x-mapping-xpath-mappings
- for (MCRCategory category : loadAllXPathMappings()) {
- if (category.getLabel(LABEL_LANG_XPATH_MAPPING).isPresent()) {
-
- String xPath = category.getLabel(LABEL_LANG_XPATH_MAPPING).get().getText();
- MCRXPathEvaluator evaluator = new MCRXPathEvaluator(new HashMap<>(), mcrmodsWrapper.getMODS());
-
- if (evaluator.test(xPath)) {
- String taskMessage = String.format(Locale.ROOT, "add x-path-mapping from '%s'",
- category.getId().toString());
- LOGGER.info(taskMessage);
- Element mappedClassification = mcrmodsWrapper.addElement("classification");
- String generator = getXPathMappingGenerator(category.getId());
- mappedClassification.setAttribute("generator", generator);
- MCRClassMapper.assignCategory(mappedClassification, category.getId());
+
+ final Map> xPathMappings = loadAllXPathMappings();
+ for (Set categoriesPerClass : xPathMappings.values()) {
+ boolean isXPathMatched = false;
+ // check x-mapping-xpath-mappings
+ for (MCRCategory category : categoriesPerClass) {
+ if (category.getLabel(LABEL_LANG_XPATH_MAPPING).isPresent()) {
+
+ String xPath = category.getLabel(LABEL_LANG_XPATH_MAPPING).get().getText();
+ MCRXPathEvaluator evaluator = new MCRXPathEvaluator(new HashMap<>(), mcrmodsWrapper.getMODS());
+
+ if (evaluator.test(xPath)) {
+ String taskMessage = String.format(Locale.ROOT, "add x-path-mapping from '%s'",
+ category.getId().toString());
+ LOGGER.info(taskMessage);
+ Element mappedClassification = mcrmodsWrapper.addElement("classification");
+ String generator = getXPathMappingGenerator(category.getId());
+ mappedClassification.setAttribute("generator", generator);
+ MCRClassMapper.assignCategory(mappedClassification, category.getId());
+ isXPathMatched = true;
+ }
+ }
+ }
+ //check x-mapping-xpath-fallback-mappings
+ if (!isXPathMatched) {
+ for (MCRCategory category : categoriesPerClass) {
+ if (category.getLabel(LABEL_LANG_XPATH_MAPPING_FALLBACK).isPresent()) {
+
+ String xPath = category.getLabel(LABEL_LANG_XPATH_MAPPING_FALLBACK).get().getText();
+ MCRXPathEvaluator evaluator = new MCRXPathEvaluator(new HashMap<>(), mcrmodsWrapper.getMODS());
+
+ if (evaluator.test(xPath)) {
+ String taskMessage = String.format(Locale.ROOT, "add x-path-mapping-fallback from '%s'",
+ category.getId().toString());
+ LOGGER.info(taskMessage);
+ Element mappedClassification = mcrmodsWrapper.addElement("classification");
+ String generator = getXPathMappingGenerator(category.getId());
+ mappedClassification.setAttribute("generator", generator);
+ MCRClassMapper.assignCategory(mappedClassification, category.getId());
+ }
+ }
}
}
}
diff --git a/mycore-mods/src/test/java/org/mycore/mods/classification/MCRClassificationMappingEventHandlerTest.java b/mycore-mods/src/test/java/org/mycore/mods/classification/MCRClassificationMappingEventHandlerTest.java
index 8b3eb2eacf..c382f9b4aa 100644
--- a/mycore-mods/src/test/java/org/mycore/mods/classification/MCRClassificationMappingEventHandlerTest.java
+++ b/mycore-mods/src/test/java/org/mycore/mods/classification/MCRClassificationMappingEventHandlerTest.java
@@ -95,7 +95,8 @@ public void testMapping() throws IOException, JDOMException, URISyntaxException
}
/**
- * Tests if an XPath-mapping is properly added into a Mods-Document.
+ * Tests if an XPath-mapping is properly added into a Mods-Document. Also tests, if multiple fallbacks per
+ * classification are considered
* @throws IOException in case of error
* @throws JDOMException in case of error
* @throws URISyntaxException in case of error
@@ -109,29 +110,103 @@ public void testXPathMapping() throws IOException, JDOMException, URISyntaxExcep
loadCategory("genre.xml");
loadCategory("orcidWorkType.xml");
+ loadCategory("dummyClassification.xml");
Document document = saxBuilder.build(classLoader.getResourceAsStream(TEST_DIRECTORY + "testMods2.xml"));
MCRObject mcro = new MCRObject();
MCRMODSWrapper mw = new MCRMODSWrapper(mcro);
mw.setMODS(document.getRootElement().detach());
- mw.setID("junit", 1);
+ mw.setID("junit", 2);
MCRClassificationMappingEventHandler mapper = new MCRClassificationMappingEventHandler();
mapper.handleObjectUpdated(null, mcro);
Document xml = mcro.createXML();
+ String expression = "//mods:classification[contains(@generator,'-mycore') and "
+ + "contains(@generator,'xpathmapping2orcidWorkType') and contains(@valueURI, 'journal-article')]";
+ XPathExpression expressionObject = XPathFactory.instance()
+ .compile(expression, Filters.element(), null, MCRConstants.MODS_NAMESPACE, MCRConstants.XLINK_NAMESPACE);
+
+ Assert.assertNotNull("The mapped classification should be in the MyCoReObject now!",
+ expressionObject.evaluateFirst(
+ xml));
+
+ expression = "//mods:classification[contains(@generator,'-mycore') and "
+ + "contains(@generator,'xpathmapping2dummyClassification') and contains(@valueURI, 'dummy-text')]";
+ expressionObject = XPathFactory.instance()
+ .compile(expression, Filters.element(), null, MCRConstants.MODS_NAMESPACE, MCRConstants.XLINK_NAMESPACE);
+
+ Assert.assertNotNull("The mapped dummy classification should be in the MyCoReObject now!",
+ expressionObject.evaluateFirst(xml));
+
+ expression = "//mods:classification[contains(@generator,'-mycore') and "
+ + "contains(@generator,'xpathmapping2dummyClassification') and contains(@valueURI, 'dummy-fbonly')]";
+ expressionObject = XPathFactory.instance()
+ .compile(expression, Filters.element(), null, MCRConstants.MODS_NAMESPACE, MCRConstants.XLINK_NAMESPACE);
+
+ Assert.assertNotNull("The mapped dummy classification should be in the MyCoReObject now!",
+ expressionObject.evaluateFirst(xml));
+
LOGGER.info(new XMLOutputter(Format.getPrettyFormat()).outputString(xml));
+ }
+
+ /**
+ * Tests if a fallback XPath-mapping is properly added into a Mods-Document if the original mapping doesn't match.
+ * @throws IOException in case of error
+ * @throws JDOMException in case of error
+ * @throws URISyntaxException in case of error
+ */
+ @Test
+ public void testXPathMappingFallback() throws IOException, JDOMException, URISyntaxException {
+ MCRSessionMgr.getCurrentSession();
+ MCRTransactionHelper.isTransactionActive();
+ ClassLoader classLoader = getClass().getClassLoader();
+ SAXBuilder saxBuilder = new SAXBuilder();
+
+ loadCategory("genre.xml");
+ loadCategory("orcidWorkType.xml");
+ loadCategory("dummyClassification.xml");
+
+ Document document = saxBuilder.build(classLoader.getResourceAsStream(TEST_DIRECTORY + "testMods3.xml"));
+ MCRObject mcro = new MCRObject();
+
+ MCRMODSWrapper mw = new MCRMODSWrapper(mcro);
+ mw.setMODS(document.getRootElement().detach());
+ mw.setID("junit", 3);
+
+ MCRClassificationMappingEventHandler mapper = new MCRClassificationMappingEventHandler();
+ mapper.handleObjectUpdated(null, mcro);
+
+ Document xml = mcro.createXML();
String expression = "//mods:classification[contains(@generator,'-mycore') and "
- + "contains(@generator,'xpathmapping2orcidWorkType') and contains(@valueURI, 'journal-article')]";
+ + "contains(@generator,'xpathmapping2orcidWorkType') and contains(@valueURI, 'online-resource')]";
XPathExpression expressionObject = XPathFactory.instance()
.compile(expression, Filters.element(), null, MCRConstants.MODS_NAMESPACE, MCRConstants.XLINK_NAMESPACE);
Assert.assertNotNull("The mapped classification should be in the MyCoReObject now!",
expressionObject.evaluateFirst(
xml));
+
+ expression = "//mods:classification[contains(@generator,'-mycore') and "
+ + "contains(@generator,'xpathmapping2dummyClassification') and contains(@valueURI, 'dummy-text')]";
+ expressionObject = XPathFactory.instance()
+ .compile(expression, Filters.element(), null, MCRConstants.MODS_NAMESPACE, MCRConstants.XLINK_NAMESPACE);
+
+ Assert.assertNotNull("The mapped dummy classification should be in the MyCoReObject now!",
+ expressionObject.evaluateFirst(xml));
+
+ expression = "//mods:classification[contains(@generator,'-mycore') and "
+ + "contains(@generator,'xpathmapping2dummyClassification') and contains(@valueURI, 'dummy-fbonly')]";
+ expressionObject = XPathFactory.instance()
+ .compile(expression, Filters.element(), null, MCRConstants.MODS_NAMESPACE, MCRConstants.XLINK_NAMESPACE);
+
+ Assert.assertNotNull("The mapped dummy classification should be in the MyCoReObject now!",
+ expressionObject.evaluateFirst(xml));
+
+ LOGGER.info(new XMLOutputter(Format.getPrettyFormat()).outputString(xml));
}
private void loadCategory(String categoryFileName) throws URISyntaxException, JDOMException, IOException {
diff --git a/mycore-mods/src/test/resources/MCRClassificationMappingEventHandlerTest/dummyClassification.xml b/mycore-mods/src/test/resources/MCRClassificationMappingEventHandlerTest/dummyClassification.xml
new file mode 100644
index 0000000000..debba5222b
--- /dev/null
+++ b/mycore-mods/src/test/resources/MCRClassificationMappingEventHandlerTest/dummyClassification.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mycore-mods/src/test/resources/MCRClassificationMappingEventHandlerTest/orcidWorkType.xml b/mycore-mods/src/test/resources/MCRClassificationMappingEventHandlerTest/orcidWorkType.xml
index 110d774239..39f007063d 100644
--- a/mycore-mods/src/test/resources/MCRClassificationMappingEventHandlerTest/orcidWorkType.xml
+++ b/mycore-mods/src/test/resources/MCRClassificationMappingEventHandlerTest/orcidWorkType.xml
@@ -10,5 +10,23 @@
description="Articles in peer-reviewed publications that disseminate the results of original research and scholarship."/>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mycore-mods/src/test/resources/MCRClassificationMappingEventHandlerTest/testMods3.xml b/mycore-mods/src/test/resources/MCRClassificationMappingEventHandlerTest/testMods3.xml
new file mode 100644
index 0000000000..34e54d929d
--- /dev/null
+++ b/mycore-mods/src/test/resources/MCRClassificationMappingEventHandlerTest/testMods3.xml
@@ -0,0 +1,12 @@
+
+
+ text
+
+
+ Test-Titel
+
+
+ de
+
+ cc_by-nc
+
diff --git a/mycore-mods/src/test/resources/mycore.properties b/mycore-mods/src/test/resources/mycore.properties
index 19c056fcec..02b1829e18 100644
--- a/mycore-mods/src/test/resources/mycore.properties
+++ b/mycore-mods/src/test/resources/mycore.properties
@@ -52,4 +52,4 @@ MCR.MODS.EnrichmentResolver.DataSource.GBV2.StopOnFirstResult=false
MCR.MODS.EnrichmentResolver.DataSource.GBV2.IdentifierTypes=isbn
MCR.MODS.EnrichmentResolver.DataSource.GBV2.isbn.URI=resource:%TestFolder%/gbv-{0}.xml
-MCR.Category.XPathMapping.ClassIDs=orcidWorkType
+MCR.Category.XPathMapping.ClassIDs=orcidWorkType,dummyClassification
diff --git a/mycore-oai/src/main/java/org/mycore/oai/classmapping/MCRClassificationMappingEventHandler.java b/mycore-oai/src/main/java/org/mycore/oai/classmapping/MCRClassificationMappingEventHandler.java
index 67b52ef10a..94b0aae22e 100644
--- a/mycore-oai/src/main/java/org/mycore/oai/classmapping/MCRClassificationMappingEventHandler.java
+++ b/mycore-oai/src/main/java/org/mycore/oai/classmapping/MCRClassificationMappingEventHandler.java
@@ -22,6 +22,10 @@
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -56,6 +60,9 @@ public class MCRClassificationMappingEventHandler extends MCREventHandlerBase {
private static final Logger LOGGER = LogManager.getLogger(MCRClassificationMappingEventHandler.class);
public static final String LABEL_LANG_XPATH_MAPPING = "x-mapping-xpath";
+
+ public static final String LABEL_LANG_XPATH_MAPPING_FALLBACK = "x-mapping-xpathfb";
+
public static final String LABEL_LANG_X_MAPPING = "x-mapping";
/** This configuration lists all eligible classifications for x-path-mapping */
@@ -168,8 +175,11 @@ private List getXMappingCategories(Document doc, MCRCategoryDAO dao
/**
* For a list of configured classifications
* (see {@link MCRClassificationMappingEventHandler#X_PATH_MAPPING_CLASSIFICATIONS}),
- * searches for categories with the label {@link MCRClassificationMappingEventHandler#LABEL_LANG_XPATH_MAPPING}
+ * searches for categories with at least one of the labels
+ * {@link MCRClassificationMappingEventHandler#LABEL_LANG_XPATH_MAPPING}
+ * or {@link MCRClassificationMappingEventHandler#LABEL_LANG_XPATH_MAPPING_FALLBACK}
* through a given DAO. The XPaths attached to those categories are then evaluated against a given document.
+ * The fallback is evaluated per classification.
* A list with all {@link MCRCategory MCRCategories} with matching XPaths is returned.
* @param doc the document to evaluate XPaths against
* @param dao the {@link MCRCategoryDAO} that gives access to all stored categories
@@ -177,22 +187,48 @@ private List getXMappingCategories(Document doc, MCRCategoryDAO dao
*/
private List getXPathMappingCategories(Document doc, MCRCategoryDAO dao) {
List listToAdd = new ArrayList<>();
- final List xPathMappingRelevantCategories = Arrays
- .stream(X_PATH_MAPPING_CLASSIFICATIONS.trim().split(",")).flatMap(
- relevantClass -> dao.getCategoriesByClassAndLang(relevantClass, LABEL_LANG_XPATH_MAPPING).stream())
- .toList();
-
- for (MCRCategory category : xPathMappingRelevantCategories) {
- if (category.getLabel(LABEL_LANG_XPATH_MAPPING).isPresent()) {
-
- String xPath = category.getLabel(LABEL_LANG_XPATH_MAPPING).get().getText();
- MCRXPathEvaluator evaluator = new MCRXPathEvaluator(new HashMap<>(), doc);
-
- if (evaluator.test(xPath)) {
- String taskMessage = String.format(Locale.ROOT, "adding x-path-mapping from '%s'",
- category.getId().toString());
- LOGGER.info(taskMessage);
- listToAdd.add(category);
+ final Map> xPathMappingRelevantCategories = Arrays
+ .stream(X_PATH_MAPPING_CLASSIFICATIONS.trim().split(",")).collect(Collectors.toMap(
+ relevantClass -> relevantClass,
+ relevantClass -> Stream.concat(
+ dao.getCategoriesByClassAndLang(relevantClass, LABEL_LANG_XPATH_MAPPING).stream(),
+ dao.getCategoriesByClassAndLang(relevantClass, LABEL_LANG_XPATH_MAPPING_FALLBACK).stream())
+ .collect(Collectors.toSet())));
+
+ // check x-mapping-xpath-mappings
+ for (Set categoriesPerClass : xPathMappingRelevantCategories.values()) {
+ boolean isXPathMatched = false;
+ for (MCRCategory category : categoriesPerClass) {
+ if (category.getLabel(LABEL_LANG_XPATH_MAPPING).isPresent()) {
+
+ String xPath = category.getLabel(LABEL_LANG_XPATH_MAPPING).get().getText();
+ MCRXPathEvaluator evaluator = new MCRXPathEvaluator(new HashMap<>(), doc);
+
+ if (evaluator.test(xPath)) {
+ String taskMessage = String.format(Locale.ROOT, "adding x-path-mapping from '%s'",
+ category.getId().toString());
+ LOGGER.info(taskMessage);
+ listToAdd.add(category);
+ isXPathMatched = true;
+ }
+ }
+ }
+ if (!isXPathMatched) {
+ //check x-mapping-xpath-fallback-mappings
+ for (MCRCategory category : categoriesPerClass) {
+ if (category.getLabel(LABEL_LANG_XPATH_MAPPING_FALLBACK).isPresent()) {
+
+ String xPath = category.getLabel(LABEL_LANG_XPATH_MAPPING_FALLBACK).get().getText();
+ MCRXPathEvaluator evaluator = new MCRXPathEvaluator(new HashMap<>(), doc);
+
+ if (evaluator.test(xPath)) {
+ String taskMessage = String.format(Locale.ROOT,
+ "adding x-path-mapping-fallback from '%s'",
+ category.getId().toString());
+ LOGGER.info(taskMessage);
+ listToAdd.add(category);
+ }
+ }
}
}
}
@@ -203,8 +239,9 @@ private List getXPathMappingCategories(Document doc, MCRCategoryDAO
* Searches a given list with {@link MCRCategory MCRCategories} for labels that signal a mapping to a
* classification. When the relevant labels are found, new mappings are added to the given
* {@link MCRMetaElement mappings-element}.
- * The relevant language labels are: {@link MCRClassificationMappingEventHandler#LABEL_LANG_X_MAPPING} and
- * {@link MCRClassificationMappingEventHandler#LABEL_LANG_XPATH_MAPPING}.
+ * The relevant language labels are: {@link MCRClassificationMappingEventHandler#LABEL_LANG_X_MAPPING},
+ * {@link MCRClassificationMappingEventHandler#LABEL_LANG_XPATH_MAPPING} and
+ * {@link MCRClassificationMappingEventHandler#LABEL_LANG_XPATH_MAPPING_FALLBACK}.
* @param mappings the element that contains all mappings
* @param categories the relevant categories that should be searched for specific language labels
*/
diff --git a/mycore-oai/src/test/java/org/mycore/oai/classmapping/MCRClassificationMappingEventHandlerTest.java b/mycore-oai/src/test/java/org/mycore/oai/classmapping/MCRClassificationMappingEventHandlerTest.java
index bfb6ba4bb3..36ea39874b 100644
--- a/mycore-oai/src/test/java/org/mycore/oai/classmapping/MCRClassificationMappingEventHandlerTest.java
+++ b/mycore-oai/src/test/java/org/mycore/oai/classmapping/MCRClassificationMappingEventHandlerTest.java
@@ -40,7 +40,7 @@ public MCRCategoryDAO getDAO() {
@Override
public void setUp() throws Exception {
super.setUp();
- MCRConfiguration2.set("MCR.Category.XPathMapping.ClassIDs", "orcidWorkType");
+ MCRConfiguration2.set("MCR.Category.XPathMapping.ClassIDs", "orcidWorkType,dummyClassification");
}
/**
@@ -58,6 +58,7 @@ public void testXMapping() throws IOException, JDOMException, URISyntaxException
loadCategory("genre.xml");
loadCategory("orcidWorkType.xml");
+ loadCategory("dummyClassification.xml");
Document document = saxBuilder.build(classLoader.getResourceAsStream(TEST_DIRECTORY + "testMcrObject.xml"));
MCRObject mcro = new MCRObject(document);
@@ -92,6 +93,60 @@ public void testXMapping() throws IOException, JDOMException, URISyntaxException
expressionObject.evaluateFirst(
xml));
+ String expression4
+ = "//mappings[@class='MCRMetaClassification']/mapping[@classid='dummyClassification' "
+ + "and @categid='dummy-article']";
+ expressionObject = XPathFactory.instance()
+ .compile(expression4, Filters.element(), null, MCRConstants.XLINK_NAMESPACE);
+ Assert.assertNotNull("The mapped dummy classification should be in the MyCoReObject now!",
+ expressionObject.evaluateFirst(
+ xml));
+
+ LOGGER.info(new XMLOutputter(Format.getPrettyFormat()).outputString(xml));
+ }
+
+ /**
+ * Tests if the XPath-mappings-fallback mechanism is working correctly and that fallbacks are
+ * evaluated per classification.
+ * @throws IOException in case of error
+ * @throws JDOMException in case of error
+ * @throws URISyntaxException in case of error
+ */
+ @Test
+ public void testXPathMappingFallback() throws IOException, JDOMException, URISyntaxException {
+ MCRSessionMgr.getCurrentSession();
+ MCRTransactionHelper.isTransactionActive();
+ ClassLoader classLoader = getClass().getClassLoader();
+ SAXBuilder saxBuilder = new SAXBuilder();
+
+ loadCategory("orcidWorkType.xml");
+ loadCategory("dummyClassification.xml");
+
+ Document document = saxBuilder.build(classLoader.getResourceAsStream(TEST_DIRECTORY + "testMcrObject2.xml"));
+ MCRObject mcro = new MCRObject(document);
+
+ MCRClassificationMappingEventHandler mapper = new MCRClassificationMappingEventHandler();
+ mapper.handleObjectUpdated(null, mcro);
+ Document xml = mcro.createXML();
+
+ String expression3
+ = "//mappings[@class='MCRMetaClassification']/mapping[@classid='orcidWorkType' "
+ + "and @categid='journal-article']";
+ XPathExpression expressionObject = XPathFactory.instance()
+ .compile(expression3, Filters.element(), null, MCRConstants.XLINK_NAMESPACE);
+ Assert.assertNotNull("The mapped classification for orcidWorkType should be in the MyCoReObject now!",
+ expressionObject.evaluateFirst(
+ xml));
+
+ String expression4
+ = "//mappings[@class='MCRMetaClassification']/mapping[@classid='dummyClassification' "
+ + "and @categid='dummy-article']";
+ expressionObject = XPathFactory.instance()
+ .compile(expression4, Filters.element(), null, MCRConstants.XLINK_NAMESPACE);
+ Assert.assertNotNull("The mapped dummy classification should be in the MyCoReObject now!",
+ expressionObject.evaluateFirst(
+ xml));
+
LOGGER.info(new XMLOutputter(Format.getPrettyFormat()).outputString(xml));
}
diff --git a/mycore-oai/src/test/resources/MCRClassificationMappingEventHandlerTest/dummyClassification.xml b/mycore-oai/src/test/resources/MCRClassificationMappingEventHandlerTest/dummyClassification.xml
new file mode 100644
index 0000000000..8ca705e7db
--- /dev/null
+++ b/mycore-oai/src/test/resources/MCRClassificationMappingEventHandlerTest/dummyClassification.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mycore-oai/src/test/resources/MCRClassificationMappingEventHandlerTest/orcidWorkType.xml b/mycore-oai/src/test/resources/MCRClassificationMappingEventHandlerTest/orcidWorkType.xml
index 33a0392750..72a5076cb1 100644
--- a/mycore-oai/src/test/resources/MCRClassificationMappingEventHandlerTest/orcidWorkType.xml
+++ b/mycore-oai/src/test/resources/MCRClassificationMappingEventHandlerTest/orcidWorkType.xml
@@ -10,6 +10,8 @@
description="Articles in peer-reviewed publications that disseminate the results of original research and scholarship." />
+
diff --git a/mycore-oai/src/test/resources/MCRClassificationMappingEventHandlerTest/testMcrObject2.xml b/mycore-oai/src/test/resources/MCRClassificationMappingEventHandlerTest/testMcrObject2.xml
new file mode 100644
index 0000000000..dc367767c1
--- /dev/null
+++ b/mycore-oai/src/test/resources/MCRClassificationMappingEventHandlerTest/testMcrObject2.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
From e42a34b9c17dd3ed9290d03c3f92ea96291b137c Mon Sep 17 00:00:00 2001
From: michael-becker <35529756+michael-becker@users.noreply.github.com>
Date: Tue, 2 Jul 2024 14:16:29 +0200
Subject: [PATCH 4/5] MCR-3092 XSL test capabilities (#2144)
* MCR-3092 add XSL testing capabilities to MCRTestCase
* MCR-3092 add new line to XSL file
* MCR-3092 use util classes for XSL/classification methods, switch parameter order in transform
* MCR-3092 update documentation/parameter names
* MCR-3092 add prepareTestDocument method for content list
* MCR-3092 add license header, rename to correct class name, use xslt version 3
* MCR-3092 detach xml content from root
---------
Co-authored-by: Michael Becker
---
.../org/mycore/common/MCRJPATestCase.java | 13 +-
.../java/org/mycore/common/MCRTestCase.java | 16 +--
.../util/MCRTestCaseClassificationUtil.java | 40 ++++++
.../common/util/MCRTestCaseXSLTUtil.java | 136 ++++++++++++++++++
.../xsl/MCRXSLClassificationTest.java | 64 +++++++++
.../classification/TestClassification.xml | 15 ++
.../xslt/functions/classificationTest.xsl | 22 +++
7 files changed, 291 insertions(+), 15 deletions(-)
create mode 100644 mycore-base/src/test/java/org/mycore/common/util/MCRTestCaseClassificationUtil.java
create mode 100644 mycore-base/src/test/java/org/mycore/common/util/MCRTestCaseXSLTUtil.java
create mode 100644 mycore-base/src/test/java/org/mycore/frontend/xsl/MCRXSLClassificationTest.java
create mode 100644 mycore-base/src/test/resources/classification/TestClassification.xml
create mode 100644 mycore-base/src/test/resources/xslt/functions/classificationTest.xsl
diff --git a/mycore-base/src/test/java/org/mycore/common/MCRJPATestCase.java b/mycore-base/src/test/java/org/mycore/common/MCRJPATestCase.java
index 3b4847f6f8..62ea03d1c9 100644
--- a/mycore-base/src/test/java/org/mycore/common/MCRJPATestCase.java
+++ b/mycore-base/src/test/java/org/mycore/common/MCRJPATestCase.java
@@ -131,18 +131,18 @@ private void doSchemaOperation(Function schemaFunction) {
protected static Optional getDefaultSchema() {
return Optional.ofNullable(MCREntityManagerProvider
- .getEntityManagerFactory()
- .getProperties()
- .get("hibernate.default_schema"))
+ .getEntityManagerFactory()
+ .getProperties()
+ .get("hibernate.default_schema"))
.map(Object::toString)
.map(schema -> quoteSchema() ? '"' + schema + '"' : schema);
}
protected static boolean quoteSchema() {
return Optional.ofNullable(MCREntityManagerProvider
- .getEntityManagerFactory()
- .getProperties()
- .get("hibernate.globally_quoted_identifiers"))
+ .getEntityManagerFactory()
+ .getProperties()
+ .get("hibernate.globally_quoted_identifiers"))
.map(Object::toString)
.map(Boolean::parseBoolean)
.orElse(Boolean.FALSE);
@@ -248,5 +248,4 @@ protected static void executeUpdate(String sql, Consumer consumer) {
}
});
}
-
}
diff --git a/mycore-base/src/test/java/org/mycore/common/MCRTestCase.java b/mycore-base/src/test/java/org/mycore/common/MCRTestCase.java
index 7e5ae22501..507b032c62 100644
--- a/mycore-base/src/test/java/org/mycore/common/MCRTestCase.java
+++ b/mycore-base/src/test/java/org/mycore/common/MCRTestCase.java
@@ -18,6 +18,14 @@
package org.mycore.common;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.rules.TemporaryFolder;
+import org.mycore.common.config.MCRConfigurationException;
+
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
@@ -32,14 +40,6 @@
import java.util.Objects;
import java.util.Optional;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.ClassRule;
-import org.junit.Rule;
-import org.junit.rules.TemporaryFolder;
-import org.mycore.common.config.MCRConfigurationException;
-
@MCRTestConfiguration(properties = {
@MCRTestProperty(key = "MCR.Metadata.Type.test", string = "true")
})
diff --git a/mycore-base/src/test/java/org/mycore/common/util/MCRTestCaseClassificationUtil.java b/mycore-base/src/test/java/org/mycore/common/util/MCRTestCaseClassificationUtil.java
new file mode 100644
index 0000000000..5c0381f27c
--- /dev/null
+++ b/mycore-base/src/test/java/org/mycore/common/util/MCRTestCaseClassificationUtil.java
@@ -0,0 +1,40 @@
+/*
+ * 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.common.util;
+
+import org.jdom2.Document;
+import org.jdom2.input.SAXBuilder;
+import org.mycore.datamodel.classifications2.MCRCategory;
+import org.mycore.datamodel.classifications2.MCRCategoryDAOFactory;
+import org.mycore.datamodel.classifications2.utils.MCRXMLTransformer;
+
+public class MCRTestCaseClassificationUtil {
+ /**
+ * Adds a classification represented by the given XML file from classpath to the system.
+ *
+ * @param resourcePath the XML classpath file containing the classification
+ */
+ public static void addClassification(String resourcePath) throws Exception {
+ Document classification = new SAXBuilder()
+ .build(MCRTestCaseClassificationUtil.class.getResourceAsStream(resourcePath));
+ MCRCategory category = MCRXMLTransformer.getCategory(classification);
+ MCRCategoryDAOFactory.getInstance().addCategory(null, category);
+ }
+
+}
diff --git a/mycore-base/src/test/java/org/mycore/common/util/MCRTestCaseXSLTUtil.java b/mycore-base/src/test/java/org/mycore/common/util/MCRTestCaseXSLTUtil.java
new file mode 100644
index 0000000000..d9e41c6dae
--- /dev/null
+++ b/mycore-base/src/test/java/org/mycore/common/util/MCRTestCaseXSLTUtil.java
@@ -0,0 +1,136 @@
+/*
+ * 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.common.util;
+
+import net.sf.saxon.jaxp.TransformerImpl;
+import net.sf.saxon.s9api.Message;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.jdom2.Content;
+import org.jdom2.Document;
+import org.jdom2.Element;
+import org.jdom2.transform.JDOMResult;
+import org.jdom2.transform.JDOMSource;
+import org.mycore.common.xml.MCRURIResolver;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.stream.StreamSource;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Provides utility methods for testing XSLT functions. The general process for XSL testing is as follows:
+ *
+ *
create a test XSL file containing match templates calling functions/templates to test
+ *
call prepareTestDocument with the rootName matching the test template (and possible XML content)
+ *
call transform with the test document and evaluate the result document
+ *
+ */
+public class MCRTestCaseXSLTUtil {
+ private static final Logger LOGGER = LogManager.getLogger();
+ private static final String ERROR_XTMM9000 = "XTMM9000";
+ private static final String ERROR_XTMM9001 = "XTMM9001";
+
+ /**
+ * Returns an XML document with a root element with the given name
+ *
+ * @param rootName the root element name of the document
+ * @return an XML document with a root element with the given name
+ */
+ public static Document prepareTestDocument(String rootName) {
+ return new Document().addContent(new Element(rootName));
+ }
+
+ /**
+ * Returns an XML document with the given name as root element and the content of the given XML tree as
+ * child element. The resulting document can be used for calling XSLT test files using template matching.
+ *
+ * @param rootName the name used as root element name
+ * @param xml the XML tree
+ * @return an XML document with the name as root element and the content of the given XML tree as children
+ */
+ public static Document prepareTestDocument(String rootName, Element xml) {
+ return prepareTestDocument(rootName, Collections.singletonList(xml));
+ }
+
+ /**
+ * Returns an XML document with the given name as root element and the content of the given XML tree as
+ * child elements. The resulting document can be used for calling XSLT test files using template matching.
+ *
+ * @param rootName the name used as root element name
+ * @param xml the XML tree
+ * @return an XML document with the name as root element and the content of the given XML tree as children
+ */
+ public static Document prepareTestDocument(String rootName, List extends Content> xml) {
+ final Element root = new Element(rootName);
+
+ for (Content xmlContent : xml) {
+ root.addContent(xmlContent.detach());
+ }
+
+ return new Document(root);
+ }
+
+ /**
+ * Transforms the XML document using the given XSL stylesheet from classpath with the given parameters.
+ *
+ * @param xml the XML document to parse
+ * @param xsl the XSL stylesheet file used for parsing
+ * @param parameters the XSL transformation parameters
+ */
+ public static Document transform(Document xml, String xsl, Map parameters)
+ throws TransformerException {
+ Source xslt = new StreamSource(MCRTestCaseXSLTUtil.class.getResourceAsStream(xsl));
+ JDOMResult result = new JDOMResult();
+
+ TransformerFactory factory = TransformerFactory.newInstance();
+ factory.setURIResolver(MCRURIResolver.instance());
+
+ Transformer transformer = factory.newTransformer(xslt);
+ parameters.forEach(transformer::setParameter);
+
+ if (transformer instanceof TransformerImpl transformerImpl) {
+ transformerImpl.getUnderlyingXsltTransformer().setMessageHandler(MCRTestCaseXSLTUtil::log);
+ }
+
+ transformer.transform(new JDOMSource(xml), result);
+
+ return new Document(result.getResult());
+ }
+
+ /**
+ * Logs an XSL message to the system logger.
+ *
+ * @param message the message to log
+ */
+ private static void log(Message message) {
+ // error codes from https://www.w3.org/2005/xqt-errors/:
+ // XTMM9000, XTMM9001 are messages; other codes are warnings/errors
+ if (StringUtils.equalsAny(message.getErrorCode().getLocalName(), ERROR_XTMM9000, ERROR_XTMM9001)) {
+ LOGGER.info(message.getContent());
+ } else {
+ LOGGER.error(message.getContent());
+ }
+ }
+}
diff --git a/mycore-base/src/test/java/org/mycore/frontend/xsl/MCRXSLClassificationTest.java b/mycore-base/src/test/java/org/mycore/frontend/xsl/MCRXSLClassificationTest.java
new file mode 100644
index 0000000000..c31aba7ef3
--- /dev/null
+++ b/mycore-base/src/test/java/org/mycore/frontend/xsl/MCRXSLClassificationTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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.frontend.xsl;
+
+import org.jdom2.Document;
+import org.jdom2.Element;
+import org.junit.Test;
+import org.mycore.common.MCRJPATestCase;
+import org.mycore.common.util.MCRTestCaseClassificationUtil;
+
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.mycore.common.util.MCRTestCaseXSLTUtil.prepareTestDocument;
+import static org.mycore.common.util.MCRTestCaseXSLTUtil.transform;
+
+public class MCRXSLClassificationTest extends MCRJPATestCase {
+ private static final String XSL = "/xslt/functions/classificationTest.xsl";
+
+ @Test
+ public void testCurrentLabelText() throws Exception {
+ MCRTestCaseClassificationUtil.addClassification("/classification/TestClassification.xml");
+ final Document testDoc = prepareTestDocument("test-current-label-text");
+
+ // we have a current lang label (de) and a default lang label (en) entry
+ Element result = transform(testDoc, XSL,
+ Map.of("classid", "TestClassification", "categid", "junit_1"))
+ .getRootElement();
+ assertEquals("junit_1 (de)", result.getText());
+
+ result = transform(testDoc, XSL,
+ Map.of("classid", "TestClassification", "categid", "junit_1", "CurrentLang", "en"))
+ .getRootElement();
+ assertEquals("junit_1 (en)", result.getText());
+
+ // we do not have a current lang label (de) entry, but default lang label (en) entry
+ result = transform(testDoc, XSL,
+ Map.of("classid", "TestClassification", "categid", "junit_2"))
+ .getRootElement();
+ assertEquals("junit_2 (en)", result.getText());
+
+ // we neither have current lang nor default lang label
+ result = transform(testDoc, XSL,
+ Map.of("classid", "TestClassification", "categid", "junit_3"))
+ .getRootElement();
+ assertEquals("??junit_3@de??", result.getText());
+ }
+}
diff --git a/mycore-base/src/test/resources/classification/TestClassification.xml b/mycore-base/src/test/resources/classification/TestClassification.xml
new file mode 100644
index 0000000000..022551338d
--- /dev/null
+++ b/mycore-base/src/test/resources/classification/TestClassification.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mycore-base/src/test/resources/xslt/functions/classificationTest.xsl b/mycore-base/src/test/resources/xslt/functions/classificationTest.xsl
new file mode 100644
index 0000000000..a21c96d498
--- /dev/null
+++ b/mycore-base/src/test/resources/xslt/functions/classificationTest.xsl
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From 92868d2e8a9d0694afa11c88fee67071105691fd Mon Sep 17 00:00:00 2001
From: Thomas Scheffler
Date: Wed, 3 Jul 2024 13:18:08 +0200
Subject: [PATCH 5/5] update maven-processor-plugin for Maven 4
---
pom.xml | 21 ++++++++-------------
1 file changed, 8 insertions(+), 13 deletions(-)
diff --git a/pom.xml b/pom.xml
index b7444a3dc0..27dfeda332 100644
--- a/pom.xml
+++ b/pom.xml
@@ -263,19 +263,7 @@ Applications based on MyCoRe use a common core, which provides the functionality
org.bsc.mavenmaven-processor-plugin
- 4.5
-
-
- jakarta.xml.bind
- jakarta.xml.bind-api
- 3.0.1
-
-
- org.hibernate.orm
- hibernate-jpamodelgen
- ${hibernate.version}
-
-
+ 5.1generate-jpa-model
@@ -284,6 +272,13 @@ Applications based on MyCoRe use a common core, which provides the functionality
generate-sources
+
+
+ org.hibernate.orm
+ hibernate-jpamodelgen
+ ${hibernate.version}
+
+ -implicit:none -AaddGeneratedAnnotation=falseorg.hibernate.jpamodelgen.JPAMetaModelEntityProcessor