Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.mycore.datamodel.metadata.validator.MCREditorOutValidator Maven / Gradle / Ivy
/*
* This file is part of *** M y C o R e ***
* See http://www.mycore.de/ for details.
*
* MyCoRe is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MyCoRe is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with MyCoRe. If not, see .
*/
package org.mycore.datamodel.metadata.validator;
import static org.jdom2.Namespace.XML_NAMESPACE;
import static org.mycore.common.MCRConstants.XLINK_NAMESPACE;
import static org.mycore.common.MCRConstants.XSI_NAMESPACE;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jdom2.Attribute;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.filter.Filters;
import org.jdom2.input.SAXBuilder;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;
import org.jdom2.xpath.XPathFactory;
import org.mycore.access.MCRAccessManager;
import org.mycore.common.MCRClassTools;
import org.mycore.common.MCRException;
import org.mycore.common.MCRSessionMgr;
import org.mycore.common.MCRUtils;
import org.mycore.common.config.MCRConfiguration2;
import org.mycore.common.content.MCRJDOMContent;
import org.mycore.datamodel.metadata.MCRMetaAccessRule;
import org.mycore.datamodel.metadata.MCRMetaAddress;
import org.mycore.datamodel.metadata.MCRMetaBoolean;
import org.mycore.datamodel.metadata.MCRMetaClassification;
import org.mycore.datamodel.metadata.MCRMetaDerivateLink;
import org.mycore.datamodel.metadata.MCRMetaEnrichedLinkID;
import org.mycore.datamodel.metadata.MCRMetaHistoryDate;
import org.mycore.datamodel.metadata.MCRMetaISO8601Date;
import org.mycore.datamodel.metadata.MCRMetaInstitutionName;
import org.mycore.datamodel.metadata.MCRMetaInterface;
import org.mycore.datamodel.metadata.MCRMetaLangText;
import org.mycore.datamodel.metadata.MCRMetaLink;
import org.mycore.datamodel.metadata.MCRMetaLinkID;
import org.mycore.datamodel.metadata.MCRMetaNumber;
import org.mycore.datamodel.metadata.MCRMetaPersonName;
import org.mycore.datamodel.metadata.MCRObject;
import org.mycore.datamodel.metadata.MCRObjectID;
import org.xml.sax.SAXParseException;
/**
* @author Thomas Scheffler (yagee)
* @version $Revision: 1 $ $Date: 08.05.2009 15:51:37 $
*/
public class MCREditorOutValidator {
private static final String CONFIG_PREFIX = "MCR.EditorOutValidator.";
private static final SAXBuilder SAX_BUILDER = new SAXBuilder();
private static Logger LOGGER = LogManager.getLogger();
private static Map VALIDATOR_MAP = getValidatorMap();
private static Map> CLASS_MAP = new HashMap<>();
private Document input;
private MCRObjectID id;
private List errorlog;
/**
* instantiate the validator with the editor input jdom_in
.
*
* id
will be set as the MCRObjectID for the resulting object
* that can be fetched with generateValidMyCoReObject()
*
* @param jdomIn
* editor input
*/
public MCREditorOutValidator(Document jdomIn, MCRObjectID id) throws JDOMException, IOException {
errorlog = new ArrayList<>();
input = jdomIn;
this.id = id;
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("XML before validation:\n{}", new XMLOutputter(Format.getPrettyFormat()).outputString(input));
}
checkObject();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("XML after validation:\n{}", new XMLOutputter(Format.getPrettyFormat()).outputString(input));
}
}
private static Map getValidatorMap() {
Map map = new HashMap<>();
map.put(MCRMetaBoolean.class.getSimpleName(), getObjectCheckInstance(MCRMetaBoolean.class));
map.put(MCRMetaPersonName.class.getSimpleName(), getObjectCheckWithLangInstance(MCRMetaPersonName.class));
map.put(MCRMetaInstitutionName.class.getSimpleName(),
getObjectCheckWithLangInstance(MCRMetaInstitutionName.class));
map.put(MCRMetaAddress.class.getSimpleName(), new MCRMetaAdressCheck());
map.put(MCRMetaNumber.class.getSimpleName(), getObjectCheckWithLangNotEmptyInstance(MCRMetaNumber.class));
map.put(MCRMetaLinkID.class.getSimpleName(), getObjectCheckWithLinksInstance(MCRMetaLinkID.class));
map.put(MCRMetaEnrichedLinkID.class.getSimpleName(),
getObjectCheckWithLinksInstance(MCRMetaEnrichedLinkID.class));
map.put(MCRMetaDerivateLink.class.getSimpleName(), getObjectCheckWithLinksInstance(MCRMetaDerivateLink.class));
map.put(MCRMetaLink.class.getSimpleName(), getObjectCheckWithLinksInstance(MCRMetaLink.class));
map.put(MCRMetaISO8601Date.class.getSimpleName(),
getObjectCheckWithLangNotEmptyInstance(MCRMetaISO8601Date.class));
map.put(MCRMetaLangText.class.getSimpleName(), getObjectCheckWithLangNotEmptyInstance(MCRMetaLangText.class));
map.put(MCRMetaAccessRule.class.getSimpleName(), getObjectCheckInstance(MCRMetaAccessRule.class));
map.put(MCRMetaClassification.class.getSimpleName(), new MCRMetaClassificationCheck());
map.put(MCRMetaHistoryDate.class.getSimpleName(), new MCRMetaHistoryDateCheck());
Map props = MCRConfiguration2.getPropertiesMap()
.entrySet()
.stream()
.filter(p -> p.getKey().startsWith(CONFIG_PREFIX + "class."))
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
for (Entry entry : props.entrySet()) {
try {
String className = entry.getKey();
className = className.substring(className.lastIndexOf('.') + 1);
LOGGER.info("Adding Validator {} for class {}", entry.getValue(), className);
@SuppressWarnings("unchecked")
Class cl = (Class) Class
.forName(entry.getValue());
map.put(className, cl.getDeclaredConstructor().newInstance());
} catch (Exception e) {
final String msg = "Cannot instantiate " + entry.getValue() + " as validator for class "
+ entry.getKey();
LOGGER.error(msg);
throw new MCRException(msg, e);
}
}
return map;
}
@SuppressWarnings("unchecked")
public static Class getClass(String mcrclass) throws ClassNotFoundException {
Class clazz = CLASS_MAP.get(mcrclass);
if (clazz == null) {
clazz = MCRClassTools.forName("org.mycore.datamodel.metadata." + mcrclass);
CLASS_MAP.put(mcrclass, clazz);
}
return clazz;
}
public static String checkMetaObject(Element datasubtag, Class metaClass,
boolean keepLang) {
if (!keepLang) {
datasubtag.removeAttribute("lang", XML_NAMESPACE);
}
MCRMetaInterface test = null;
try {
test = metaClass.getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new MCRException("Could not instantiate " + metaClass.getCanonicalName());
}
test.setFromDOM(datasubtag);
test.validate();
return null;
}
public static String checkMetaObjectWithLang(Element datasubtag, Class metaClass) {
if (datasubtag.getAttribute("lang") != null) {
datasubtag.getAttribute("lang").setNamespace(XML_NAMESPACE);
LOGGER.warn("namespace add for xml:lang attribute in {}", datasubtag.getName());
}
return checkMetaObject(datasubtag, metaClass, true);
}
public static String checkMetaObjectWithLangNotEmpty(Element datasubtag,
Class metaClass) {
String text = datasubtag.getTextTrim();
if (text == null || text.length() == 0) {
return "Element " + datasubtag.getName() + " has no text.";
}
return checkMetaObjectWithLang(datasubtag, metaClass);
}
public static String checkMetaObjectWithLinks(Element datasubtag, Class metaClass) {
if (datasubtag.getAttributeValue("href") == null
&& datasubtag.getAttributeValue("href", XLINK_NAMESPACE) == null) {
return datasubtag.getName() + " has no href attribute defined";
}
if (datasubtag.getAttribute("xtype") != null) {
datasubtag.getAttribute("xtype").setNamespace(XLINK_NAMESPACE).setName("type");
} else if (datasubtag.getAttribute("type") != null
&& datasubtag.getAttribute("type", XLINK_NAMESPACE) == null) {
datasubtag.getAttribute("type").setNamespace(XLINK_NAMESPACE);
LOGGER.warn("namespace add for xlink:type attribute in {}", datasubtag.getName());
}
if (datasubtag.getAttribute("href") != null) {
datasubtag.getAttribute("href").setNamespace(XLINK_NAMESPACE);
LOGGER.warn("namespace add for xlink:href attribute in {}", datasubtag.getName());
}
if (datasubtag.getAttribute("title") != null) {
datasubtag.getAttribute("title").setNamespace(XLINK_NAMESPACE);
LOGGER.warn("namespace add for xlink:title attribute in {}", datasubtag.getName());
}
if (datasubtag.getAttribute("label") != null) {
datasubtag.getAttribute("label").setNamespace(XLINK_NAMESPACE);
LOGGER.warn("namespace add for xlink:label attribute in {}", datasubtag.getName());
}
return checkMetaObject(datasubtag, metaClass, false);
}
static MCREditorMetadataValidator getObjectCheckInstance(final Class clazz) {
return datasubtag -> MCREditorOutValidator.checkMetaObject(datasubtag, clazz, false);
}
static MCREditorMetadataValidator getObjectCheckWithLangInstance(final Class clazz) {
return datasubtag -> MCREditorOutValidator.checkMetaObjectWithLang(datasubtag, clazz);
}
static MCREditorMetadataValidator getObjectCheckWithLangNotEmptyInstance(
final Class clazz) {
return datasubtag -> MCREditorOutValidator.checkMetaObjectWithLangNotEmpty(datasubtag, clazz);
}
static MCREditorMetadataValidator getObjectCheckWithLinksInstance(final Class clazz) {
return datasubtag -> MCREditorOutValidator.checkMetaObjectWithLinks(datasubtag, clazz);
}
/**
* The method add a default ACL-block.
*/
public static void setDefaultDerivateACLs(Element service) {
// Read stylesheet and add user
InputStream aclxml = MCREditorOutValidator.class.getResourceAsStream("/editor_default_acls_derivate.xml");
if (aclxml == null) {
LOGGER.warn("Can't find default derivate ACL file editor_default_acls_derivate.xml.");
return;
}
try {
Document xml = SAX_BUILDER.build(aclxml);
Element acls = xml.getRootElement().getChild("servacls");
if (acls != null) {
service.addContent(acls.detach());
}
} catch (Exception e) {
LOGGER.warn("Error while parsing file editor_default_acls_derivate.xml.");
}
}
/**
* tries to generate a valid MCRObject as JDOM Document.
*
* @return MCRObject
*/
public Document generateValidMyCoReObject() throws JDOMException, SAXParseException, IOException {
MCRObject obj;
// load the JDOM object
XPathFactory.instance()
.compile("/mycoreobject/*/*/*/@editor.output", Filters.attribute())
.evaluate(input)
.forEach(Attribute::detach);
try {
byte[] xml = new MCRJDOMContent(input).asByteArray();
obj = new MCRObject(xml, true);
} catch (SAXParseException e) {
XMLOutputter xout = new XMLOutputter(Format.getPrettyFormat());
LOGGER.warn("Failure while parsing document:\n{}", xout.outputString(input));
throw e;
}
Date curTime = new Date();
obj.getService().setDate("modifydate", curTime);
// return the XML tree
input = obj.createXML();
return input;
}
/**
* returns a List of Error log entries
*
* @return log entries for the whole validation process
*/
public List getErrorLog() {
return errorlog;
}
/**
* @throws IOException
* @throws JDOMException
*
*/
private void checkObject() throws JDOMException, IOException {
// add the namespaces (this is a workaround)
Element root = input.getRootElement();
root.addNamespaceDeclaration(XLINK_NAMESPACE);
root.addNamespaceDeclaration(XSI_NAMESPACE);
// set the schema
String mcrSchema = "datamodel-" + id.getTypeId() + ".xsd";
root.setAttribute("noNamespaceSchemaLocation", mcrSchema, XSI_NAMESPACE);
// check the label
String label = MCRUtils.filterTrimmedNotEmpty(root.getAttributeValue("label"))
.orElse(null);
if (label == null) {
root.setAttribute("label", id.toString());
}
// remove the path elements from the incoming
Element pathes = root.getChild("pathes");
if (pathes != null) {
root.removeChildren("pathes");
}
Element structure = root.getChild("structure");
if (structure == null) {
root.addContent(new Element("structure"));
} else {
checkObjectStructure(structure);
}
Element metadata = root.getChild("metadata");
checkObjectMetadata(metadata);
Element service = root.getChild("service");
checkObjectService(root, service);
}
/**
* @param datatag
*/
private boolean checkMetaTags(Element datatag) {
String mcrclass = datatag.getAttributeValue("class");
List datataglist = datatag.getChildren();
Iterator datatagIt = datataglist.iterator();
while (datatagIt.hasNext()) {
Element datasubtag = datatagIt.next();
MCREditorMetadataValidator validator = VALIDATOR_MAP.get(mcrclass);
String returns = null;
if (validator != null) {
returns = validator.checkDataSubTag(datasubtag);
} else {
LOGGER.warn("Tag <{}> of type {} has no validator defined, fallback to default behaviour",
datatag.getName(), mcrclass);
// try to create MCRMetaInterface instance
try {
Class metaClass = getClass(mcrclass);
// just checks if class would validate this element
returns = checkMetaObject(datasubtag, metaClass, true);
} catch (ClassNotFoundException e) {
throw new MCRException("Failure while trying fallback. Class not found: " + mcrclass, e);
}
}
if (returns != null) {
datatagIt.remove();
final String msg = datatag.getName() + ": " + returns;
errorlog.add(msg);
}
}
return datatag.getChildren().size() != 0;
}
/**
* @param service
* @throws IOException
* @throws JDOMException
*/
private void checkObjectService(Element root, Element service) throws JDOMException, IOException {
if (service == null) {
service = new Element("service");
root.addContent(service);
}
List servicelist = service.getChildren();
boolean hasacls = false;
for (Element datatag : servicelist) {
checkMetaTags(datatag);
}
Collection li = MCRAccessManager.getPermissionsForID(id.toString());
if (li != null && !li.isEmpty()) {
hasacls = true;
}
if (service.getChild("servacls") == null && !hasacls) {
setDefaultObjectACLs(service);
}
}
/**
* The method add a default ACL-block.
*
* @param service
* @throws IOException
* @throws JDOMException
*/
private void setDefaultObjectACLs(Element service) throws JDOMException, IOException {
if (!MCRConfiguration2.getBoolean("MCR.Access.AddObjectDefaultRule").orElse(true)) {
LOGGER.info("Adding object default acl rule is disabled.");
return;
}
String resourcetype = "/editor_default_acls_" + id.getTypeId() + ".xml";
String resourcebase = "/editor_default_acls_" + id.getBase() + ".xml";
// Read stylesheet and add user
InputStream aclxml = MCREditorOutValidator.class.getResourceAsStream(resourcebase);
if (aclxml == null) {
aclxml = MCREditorOutValidator.class.getResourceAsStream(resourcetype);
if (aclxml == null) {
LOGGER.warn("Can't find default object ACL file {} or {}", resourcebase.substring(1),
resourcetype.substring(1));
String resource = "/editor_default_acls.xml"; // fallback
aclxml = MCREditorOutValidator.class.getResourceAsStream(resource);
if (aclxml == null) {
return;
}
}
}
Document xml = SAX_BUILDER.build(aclxml);
Element acls = xml.getRootElement().getChild("servacls");
if (acls == null) {
return;
}
for (Element acl : acls.getChildren()) {
Element condition = acl.getChild("condition");
if (condition == null) {
continue;
}
Element rootbool = condition.getChild("boolean");
if (rootbool == null) {
continue;
}
for (Element orbool : rootbool.getChildren("boolean")) {
for (Element firstcond : orbool.getChildren("condition")) {
if (firstcond == null) {
continue;
}
String value = firstcond.getAttributeValue("value");
if (value == null) {
continue;
}
if (value.equals("$CurrentUser")) {
String thisuser = MCRSessionMgr.getCurrentSession().getUserInformation().getUserID();
firstcond.setAttribute("value", thisuser);
continue;
}
if (value.equals("$CurrentGroup")) {
throw new MCRException(
"The parameter $CurrentGroup in default ACLs is not supported as of MyCoRe 2014.06"
+ " because it is not supported in Servlet API 3.0");
}
int i = value.indexOf("$CurrentIP");
if (i != -1) {
String thisip = MCRSessionMgr.getCurrentSession().getCurrentIP();
firstcond.setAttribute("value",
value.substring(0, i) + thisip + value.substring(i + 10));
}
}
}
}
service.addContent(acls.detach());
}
/**
* @param metadata
*/
private void checkObjectMetadata(Element metadata) {
if (metadata.getAttribute("lang") != null) {
metadata.getAttribute("lang").setNamespace(XML_NAMESPACE);
}
List metadatalist = metadata.getChildren();
Iterator metaIt = metadatalist.iterator();
while (metaIt.hasNext()) {
Element datatag = metaIt.next();
if (!checkMetaTags(datatag)) {
// e.g. datatag is empty
LOGGER.debug("Removing element :{}", datatag.getName());
metaIt.remove();
}
}
}
private void checkObjectStructure(Element structure) {
// e.g. datatag is empty
structure.getChildren().removeIf(datatag -> !checkMetaTags(datatag));
}
static class MCRMetaHistoryDateCheck implements MCREditorMetadataValidator {
public String checkDataSubTag(Element datasubtag) {
Element[] children = datasubtag.getChildren("text").toArray(Element[]::new);
int textCount = children.length;
for (int i = 0; i < children.length; i++) {
Element child = children[i];
String text = child.getTextTrim();
if (text == null || text.length() == 0) {
child.detach();
textCount--;
continue;
}
if (child.getAttribute("lang") != null) {
child.getAttribute("lang").setNamespace(XML_NAMESPACE);
LOGGER.warn("namespace add for xml:lang attribute in {}", datasubtag.getName());
}
}
if (textCount == 0) {
return "history date is empty";
}
return checkMetaObjectWithLang(datasubtag, MCRMetaHistoryDate.class);
}
}
static class MCRMetaClassificationCheck implements MCREditorMetadataValidator {
public String checkDataSubTag(Element datasubtag) {
String categid = datasubtag.getAttributeValue("categid");
if (categid == null) {
return "Attribute categid is empty";
}
return checkMetaObject(datasubtag, MCRMetaClassification.class, false);
}
}
static class MCRMetaAdressCheck implements MCREditorMetadataValidator {
public String checkDataSubTag(Element datasubtag) {
if (datasubtag.getChildren().size() == 0) {
return "adress is empty";
}
return checkMetaObjectWithLang(datasubtag, MCRMetaAddress.class);
}
}
static class MCRMetaPersonNameCheck implements MCREditorMetadataValidator {
public String checkDataSubTag(Element datasubtag) {
if (datasubtag.getChildren().size() == 0) {
return "person name is empty";
}
return checkMetaObjectWithLang(datasubtag, MCRMetaAddress.class);
}
}
}