1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.mycore.mods;
20
21 import java.util.ArrayList;
22 import java.util.Collections;
23 import java.util.HashMap;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Optional;
28 import java.util.Set;
29 import java.util.stream.Collectors;
30
31 import org.jdom2.Content;
32 import org.jdom2.Element;
33 import org.jdom2.JDOMException;
34 import org.jdom2.filter.Filters;
35 import org.jdom2.xpath.XPathExpression;
36 import org.jdom2.xpath.XPathFactory;
37 import org.mycore.common.MCRConstants;
38 import org.mycore.common.MCRException;
39 import org.mycore.common.config.MCRConfiguration2;
40 import org.mycore.datamodel.classifications2.MCRCategoryID;
41 import org.mycore.datamodel.metadata.MCRMetaElement;
42 import org.mycore.datamodel.metadata.MCRMetaXML;
43 import org.mycore.datamodel.metadata.MCRObject;
44 import org.mycore.datamodel.metadata.MCRObjectID;
45 import org.mycore.datamodel.metadata.MCRObjectMetadata;
46 import org.mycore.datamodel.metadata.MCRObjectService;
47 import org.mycore.mods.classification.MCRClassMapper;
48
49
50
51
52
53 public class MCRMODSWrapper {
54
55
56 private static final String LINKED_RELATED_ITEMS = "mods:relatedItem[@type='host'"
57 + " and ancestor::mycoreobject/structure/parents/parent or"
58 + " string-length(substring-after(@xlink:href,'_')) > 0 and"
59 + " string-length(substring-after(substring-after(@xlink:href,'_'), '_')) > 0 and"
60 + " number(substring-after(substring-after(@xlink:href,'_'),'_')) > 0 and" + " contains('"
61 + MCRMODSRelationshipType.xPathList() + "', @type)]";
62
63 private static final String MODS_CONTAINER = "modsContainer";
64
65 private static final String DEF_MODS_CONTAINER = "def.modsContainer";
66
67 public static final String MODS_OBJECT_TYPE = MCRConfiguration2.getStringOrThrow("MCR.MODS.NewObjectType");
68
69 private static final String MODS_DATAMODEL = "datamodel-mods.xsd";
70
71 private static List<String> topLevelElementOrder = new ArrayList<>();
72
73 private static Set<String> SUPPORTED_TYPES = MCRConfiguration2
74 .getOrThrow("MCR.MODS.Types", MCRConfiguration2::splitValue)
75 .collect(Collectors.toSet());
76
77 private MCRObject object;
78
79 static {
80 topLevelElementOrder.add("typeOfResource");
81 topLevelElementOrder.add("titleInfo");
82 topLevelElementOrder.add("name");
83 topLevelElementOrder.add("genre");
84 topLevelElementOrder.add("originInfo");
85 topLevelElementOrder.add("language");
86 topLevelElementOrder.add("abstract");
87 topLevelElementOrder.add("note");
88 topLevelElementOrder.add("subject");
89 topLevelElementOrder.add("classification");
90 topLevelElementOrder.add("relatedItem");
91 topLevelElementOrder.add("identifier");
92 topLevelElementOrder.add("location");
93 topLevelElementOrder.add("accessCondition");
94 }
95
96 private static int getRankOf(Element topLevelElement) {
97 return topLevelElementOrder.indexOf(topLevelElement.getName());
98 }
99
100 public static MCRObject wrapMODSDocument(Element modsDefinition, String projectID) {
101 MCRMODSWrapper wrapper = new MCRMODSWrapper();
102 wrapper.setID(projectID, 0);
103 wrapper.setMODS(modsDefinition);
104 return wrapper.getMCRObject();
105 }
106
107
108
109
110
111
112 public static boolean isSupported(MCRObject obj) {
113 if (isSupported(obj.getId())) {
114 return true;
115 }
116 return obj.getMetadata() != null && obj.getMetadata().getMetadataElement(DEF_MODS_CONTAINER) != null
117 && obj.getMetadata().getMetadataElement(DEF_MODS_CONTAINER).getElementByName(MODS_CONTAINER) != null;
118 }
119
120
121
122
123
124
125 public static boolean isSupported(MCRObjectID id) {
126 return SUPPORTED_TYPES.contains(id.getTypeId());
127 }
128
129 public MCRMODSWrapper() {
130 this(new MCRObject());
131 }
132
133 public MCRMODSWrapper(MCRObject object) {
134 this.object = object;
135 if (object.getSchema() == null || object.getSchema().isEmpty()) {
136 object.setSchema(MODS_DATAMODEL);
137 }
138 }
139
140
141
142
143 public Element getMODS() {
144 try {
145 MCRMetaXML mx = (MCRMetaXML) (object.getMetadata().getMetadataElement(DEF_MODS_CONTAINER).getElement(0));
146 for (Content content : mx.getContent()) {
147 if (content instanceof Element) {
148 return (Element) content;
149 }
150 }
151 } catch (NullPointerException | IndexOutOfBoundsException e) {
152
153 }
154 return null;
155 }
156
157 public MCRObject getMCRObject() {
158 return object;
159 }
160
161 public MCRObjectID setID(String projectID, int id) {
162 MCRObjectID objID = MCRObjectID.getInstance(MCRObjectID.formatID(projectID, MODS_OBJECT_TYPE, id));
163 object.setId(objID);
164 return objID;
165 }
166
167 public void setMODS(Element mods) {
168 MCRObjectMetadata om = object.getMetadata();
169 if (om.getMetadataElement(DEF_MODS_CONTAINER) != null) {
170 om.removeMetadataElement(DEF_MODS_CONTAINER);
171 }
172
173 MCRMetaXML modsContainer = new MCRMetaXML(MODS_CONTAINER, null, 0);
174 List<MCRMetaXML> list = Collections.nCopies(1, modsContainer);
175 MCRMetaElement defModsContainer = new MCRMetaElement(MCRMetaXML.class, DEF_MODS_CONTAINER, false, true, list);
176 om.setMetadataElement(defModsContainer);
177
178 modsContainer.addContent(mods);
179 }
180
181 private XPathExpression<Element> buildXPath(String xPath) throws JDOMException {
182 return XPathFactory.instance().compile(xPath, Filters.element(), null, MCRConstants.MODS_NAMESPACE,
183 MCRConstants.XLINK_NAMESPACE);
184 }
185
186 public Element getElement(String xPath) {
187 try {
188 return buildXPath(xPath).evaluateFirst(getMODS());
189 } catch (JDOMException ex) {
190 String msg = "Could not get MODS element from " + xPath;
191 throw new MCRException(msg, ex);
192 }
193 }
194
195 public List<Element> getElements(String xPath) {
196 try {
197 Element eMODS = getMODS();
198 if (eMODS != null) {
199 return buildXPath(xPath).evaluate(eMODS);
200 } else {
201 return Collections.emptyList();
202 }
203 } catch (JDOMException ex) {
204 String msg = "Could not get elements at " + xPath;
205 throw new MCRException(msg, ex);
206 }
207 }
208
209 public List<Element> getLinkedRelatedItems() {
210 return getElements(LINKED_RELATED_ITEMS);
211 }
212
213 public String getElementValue(String xPath) {
214 Element element = getElement(xPath);
215 return (element == null ? null : element.getTextTrim());
216 }
217
218
219
220
221
222 public Optional<Element> setElement(String elementName, String elementValue, Map<String, String> attributes) {
223 boolean isAttributeDataPresent = attributes != null && !attributes.isEmpty();
224 boolean isValuePresent = elementValue != null && !elementValue.isEmpty();
225
226 if (!isValuePresent && !isAttributeDataPresent) {
227 return Optional.empty();
228 }
229
230 StringBuilder xPath = new StringBuilder("mods:");
231 xPath.append(elementName);
232
233
234 if (isAttributeDataPresent) {
235
236 xPath.append('[');
237 Iterator<Map.Entry<String, String>> attributeIterator = attributes.entrySet().iterator();
238 while (attributeIterator.hasNext()) {
239 Map.Entry<String, String> attribute = attributeIterator.next();
240 xPath.append('@').append(attribute.getKey()).append("='").append(attribute.getValue()).append('\'');
241
242 if (attributeIterator.hasNext()) {
243 xPath.append(" and ");
244 }
245 }
246 xPath.append(']');
247 }
248 Element element = getElement(xPath.toString());
249
250 if (element == null) {
251 element = addElement(elementName);
252 if (isAttributeDataPresent) {
253 for (Map.Entry<String, String> entry : attributes.entrySet()) {
254 element.setAttribute(entry.getKey(), entry.getValue());
255 }
256 }
257 }
258
259 if (isValuePresent) {
260 element.setText(elementValue.trim());
261 }
262
263 return Optional.of(element);
264 }
265
266 public Optional<Element> setElement(String elementName, String attributeName, String attributeValue,
267 String elementValue) {
268 Map<String, String> attributes = Collections.emptyMap();
269 if (attributeName != null && attributeValue != null) {
270 attributes = new HashMap<>();
271 attributes.put(attributeName, attributeValue);
272 }
273 return setElement(elementName, elementValue, attributes);
274 }
275
276 public Optional<Element> setElement(String elementName, String elementValue) {
277 return setElement(elementName, null, null, elementValue);
278 }
279
280 public Element addElement(String elementName) {
281 Element element = new Element(elementName, MCRConstants.MODS_NAMESPACE);
282 insertTopLevelElement(element);
283 return element;
284 }
285
286 public void addElement(Element element) {
287 if (!element.getNamespace().equals(MCRConstants.MODS_NAMESPACE)) {
288 throw new IllegalArgumentException("given element is no mods element");
289 }
290
291 insertTopLevelElement(element);
292 }
293
294 private void insertTopLevelElement(Element element) {
295 int rankOfNewElement = getRankOf(element);
296 List<Element> topLevelElements = getMODS().getChildren();
297 for (int pos = 0; pos < topLevelElements.size(); pos++) {
298 if (getRankOf(topLevelElements.get(pos)) > rankOfNewElement) {
299 getMODS().addContent(pos, element);
300 return;
301 }
302 }
303
304 getMODS().addContent(element);
305 }
306
307 public void removeElements(String xPath) {
308 Iterator<Element> selected;
309 try {
310 selected = buildXPath(xPath).evaluate(getMODS()).iterator();
311 } catch (JDOMException ex) {
312 String msg = "Could not remove elements at " + xPath;
313 throw new MCRException(msg, ex);
314 }
315
316 while (selected.hasNext()) {
317 Element element = selected.next();
318 element.detach();
319 }
320 }
321
322 public void removeInheritedMetadata() {
323 String xPath = LINKED_RELATED_ITEMS + "/*[local-name()!='part']";
324 removeElements(xPath);
325 }
326
327 public String getServiceFlag(String type) {
328 MCRObjectService os = object.getService();
329 return (os.isFlagTypeSet(type) ? os.getFlags(type).get(0) : null);
330 }
331
332 public void setServiceFlag(String type, String value) {
333 MCRObjectService os = object.getService();
334 if (os.isFlagTypeSet(type)) {
335 os.removeFlags(type);
336 }
337 if ((value != null) && !value.trim().isEmpty()) {
338 os.addFlag(type, value.trim());
339 }
340 }
341
342 public List<MCRCategoryID> getMcrCategoryIDs() {
343 final List<Element> categoryNodes = getCategoryNodes();
344 final List<MCRCategoryID> categories = new ArrayList<>(categoryNodes.size());
345 for (Element node : categoryNodes) {
346 final MCRCategoryID categoryID = MCRClassMapper.getCategoryID(node);
347 if (categoryID != null) {
348 categories.add(categoryID);
349 }
350 }
351 return categories;
352 }
353
354 private List<Element> getCategoryNodes() {
355 return getElements(
356 "mods:typeOfResource | mods:accessCondition | .//*[(@authority or @authorityURI)"
357 + " and not(ancestor::mods:relatedItem)]");
358 }
359 }