gt:         Element metadata = root.getChild("metadata");
context: /*
   * 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 <http://www.gnu.org/licenses/>.
   */
  
  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.access.MCRRuleAccessInterface;
  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<String, MCREditorMetadataValidator> VALIDATOR_MAP = getValidatorMap();
  
      private static Map<String, Class<? extends MCRMetaInterface>> CLASS_MAP = new HashMap<>();
  
      private Document input;
  
      private MCRObjectID id;
  
      private List<String> errorlog;
  
      /**
       * instantiate the validator with the editor input <code>jdom_in</code>.
       * 
       * <code>id</code> will be set as the MCRObjectID for the resulting object
       * that can be fetched with <code>generateValidMyCoReObject()</code>
       * 
       * @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<String, MCREditorMetadataValidator> getValidatorMap() {
          Map<String, MCREditorMetadataValidator> 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<String, String> props = MCRConfiguration2.getPropertiesMap()
              .entrySet()
              .stream()
              .filter(p -> p.getKey().startsWith(CONFIG_PREFIX + "class."))
              .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
          for (Entry<String, String> 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<? extends MCREditorMetadataValidator> cl = (Class<? extends MCREditorMetadataValidator>) 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<? extends MCRMetaInterface> getClass(String mcrclass) throws ClassNotFoundException {
          Class<? extends MCRMetaInterface> 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<? extends MCRMetaInterface> 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<? extends MCRMetaInterface> 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<? extends MCRMetaInterface> 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<? extends MCRMetaInterface> 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<? extends MCRMetaInterface> clazz) {
          return datasubtag -> MCREditorOutValidator.checkMetaObject(datasubtag, clazz, false);
      }
  
      static MCREditorMetadataValidator getObjectCheckWithLangInstance(final Class<? extends MCRMetaInterface> clazz) {
          return datasubtag -> MCREditorOutValidator.checkMetaObjectWithLang(datasubtag, clazz);
      }
  
      static MCREditorMetadataValidator getObjectCheckWithLangNotEmptyInstance(
          final Class<? extends MCRMetaInterface> clazz) {
          return datasubtag -> MCREditorOutValidator.checkMetaObjectWithLangNotEmpty(datasubtag, clazz);
      }
  
      static MCREditorMetadataValidator getObjectCheckWithLinksInstance(final Class<? extends MCRMetaInterface> 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<String> 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);
          }
  