CvMappingValidator.java

  1. /*
  2.  * Copyright 2018 Leibniz-Institut für Analytische Wissenschaften – ISAS – e.V..
  3.  *
  4.  * Licensed under the Apache License, Version 2.0 (the "License");
  5.  * you may not use this file except in compliance with the License.
  6.  * You may obtain a copy of the License at
  7.  *
  8.  *      http://www.apache.org/licenses/LICENSE-2.0
  9.  *
  10.  * Unless required by applicable law or agreed to in writing, software
  11.  * distributed under the License is distributed on an "AS IS" BASIS,
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  * See the License for the specific language governing permissions and
  14.  * limitations under the License.
  15.  */
  16. package de.isas.mztab2.validation;

  17. import de.isas.lipidomics.mztab2.validation.Validator;
  18. import de.isas.mztab2.cvmapping.CvMappingUtils;
  19. import de.isas.mztab2.cvmapping.CvParameterLookupService;
  20. import de.isas.mztab2.cvmapping.JxPathElement;
  21. import de.isas.mztab2.cvmapping.RemoveUserParams;
  22. import de.isas.mztab2.cvmapping.RuleEvaluationResult;
  23. import de.isas.mztab2.model.MzTab;
  24. import de.isas.mztab2.model.Parameter;
  25. import de.isas.mztab2.model.ValidationMessage;
  26. import de.isas.mztab2.validation.handlers.AndValidationHandler;
  27. import de.isas.mztab2.validation.handlers.EmptyRuleHandler;
  28. import de.isas.mztab2.validation.handlers.ExtraParametersValidationHandler;
  29. import de.isas.mztab2.validation.handlers.OrValidationHandler;
  30. import de.isas.mztab2.validation.handlers.ResolvingCvRuleHandler;
  31. import de.isas.mztab2.validation.handlers.SharedParametersValidationHandler;
  32. import de.isas.mztab2.validation.handlers.XorValidationHandler;
  33. import info.psidev.cvmapping.CvMapping;
  34. import info.psidev.cvmapping.CvMappingRule;
  35. import java.io.File;
  36. import java.net.URL;
  37. import java.util.Arrays;
  38. import java.util.Collections;
  39. import java.util.LinkedList;
  40. import java.util.List;
  41. import javax.xml.bind.JAXBContext;
  42. import javax.xml.bind.JAXBException;
  43. import javax.xml.bind.Unmarshaller;
  44. import lombok.extern.slf4j.Slf4j;
  45. import org.apache.commons.jxpath.JXPathContext;
  46. import org.apache.commons.jxpath.Pointer;
  47. import org.apache.commons.lang3.tuple.Pair;
  48. import uk.ac.ebi.pride.jmztab2.utils.errors.CrossCheckErrorType;
  49. import uk.ac.ebi.pride.jmztab2.utils.errors.MZTabError;
  50. import uk.ac.ebi.pride.utilities.ols.web.service.client.OLSClient;
  51. import uk.ac.ebi.pride.utilities.ols.web.service.config.OLSWsConfig;

  52. /**
  53.  * Validator implementation that uses a provided xml mapping file with rules for
  54.  * required, recommended and optional CV parameters to assert that an mzTab
  55.  * follows these rules.
  56.  *
  57.  * First, all preValidators are run, then, the cv parameter validation is executed, before finally,
  58.  * the postValidators are run. Each validator can add validation messages to the output.
  59.  *
  60.  * @author nilshoffmann
  61.  */
  62. @Slf4j
  63. @lombok.Builder()
  64. public class CvMappingValidator implements Validator<MzTab> {

  65.     private final CvMapping mapping;
  66.     private final CvRuleHandler ruleHandler;
  67.     private final boolean errorIfTermNotInRule;
  68.     private final CvTermValidationHandler andHandler;
  69.     private final CvTermValidationHandler orHandler;
  70.     private final CvTermValidationHandler xorHandler;
  71.     private final CvTermValidationHandler extraHandler;
  72.     private final CvTermValidationHandler sharedHandler;
  73.     private final EmptyRuleHandler emptyRuleHandler;
  74.     private final RemoveUserParams cvTermSelectionHandler;
  75.     private final List<Validator<MzTab>> preValidators = new LinkedList<>();
  76.     private final List<Validator<MzTab>> postValidators = new LinkedList<>();

  77.     /**
  78.      * Create a new instance of CvMappingValidator.
  79.      *
  80.      * Uses a default instance of the {@link CvParameterLookupService}.
  81.      *
  82.      * @param mappingFile the mapping file to use
  83.      * @param errorIfTermNotInRule raise an error if a term is not defined within an otherwise matching rule for the element
  84.      * @return a new CvMappingValidator instance
  85.      * @throws JAXBException if errors occur during unmarshalling of the mapping xml file.
  86.      */
  87.     public static CvMappingValidator of(File mappingFile,
  88.         boolean errorIfTermNotInRule) throws JAXBException {
  89.         OLSWsConfig config = new OLSWsConfig();
  90.         OLSClient client = new OLSClient(config);
  91.         CvParameterLookupService service = new CvParameterLookupService(client);
  92.         return of(mappingFile, service, errorIfTermNotInRule);
  93.     }

  94.     /**
  95.      * Create a new instance of CvMappingValidator.
  96.      *
  97.      * Uses the provided {@link CvParameterLookupService}.
  98.      *
  99.      * @param mappingFile the mapping file to use
  100.      * @param client the ontology lookup service client
  101.      * @param errorIfTermNotInRule raise an error if a term is not defined within an otherwise matching rule for the element
  102.      * @return a new CvMappingValidator instance
  103.      * @throws JAXBException if errors occur during unmarshalling of the mapping xml file.
  104.      */
  105.     public static CvMappingValidator of(File mappingFile,
  106.         CvParameterLookupService client, boolean errorIfTermNotInRule) throws JAXBException {

  107.         JAXBContext jaxbContext = JAXBContext.newInstance(CvMapping.class);
  108.         Unmarshaller u = jaxbContext.createUnmarshaller();
  109.         CvMapping mapping = (CvMapping) u.unmarshal(mappingFile);
  110.         return new CvMappingValidator.CvMappingValidatorBuilder().mapping(
  111.             mapping).
  112.             ruleHandler(new ResolvingCvRuleHandler(client)).
  113.             errorIfTermNotInRule(errorIfTermNotInRule).
  114.             andHandler(new AndValidationHandler()).
  115.             orHandler(new OrValidationHandler()).
  116.             xorHandler(new XorValidationHandler()).
  117.             extraHandler(new ExtraParametersValidationHandler()).
  118.             sharedHandler(new SharedParametersValidationHandler()).
  119.             cvTermSelectionHandler(new RemoveUserParams()).
  120.             emptyRuleHandler(new EmptyRuleHandler()).
  121.             build().
  122.             withPreValidator(new CvDefinitionValidationHandler());
  123.     }

  124.     /**
  125.      * Add the provided validator implementation to the list of validators that run <b>first</b>.
  126.      * @param preValidator the validator
  127.      * @return an instance of this object
  128.      */
  129.     public CvMappingValidator withPreValidator(Validator<MzTab> preValidator) {
  130.         preValidators.add(preValidator);
  131.         return this;
  132.     }

  133.     /**
  134.      * Add the provided validator implementation to the list of validators that run <b>last</b>.
  135.      * @param postValidator the validator
  136.      * @return an instance of this object
  137.      */
  138.     public CvMappingValidator withPostValidator(Validator<MzTab> postValidator) {
  139.         postValidators.add(postValidator);
  140.         return this;
  141.     }

  142.     /**
  143.      * Create a new instance of CvMappingValidator.
  144.      *
  145.      * Uses a default instance of the {@link CvParameterLookupService}.
  146.      *
  147.      * @param mappingFile the mapping file URL to use
  148.      * @param errorIfTermNotInRule raise an error if a term is not defined within an otherwise matching rule for the element
  149.      * @return a new CvMappingValidator instance
  150.      * @throws JAXBException if errors occur during unmarshalling of the mapping xml file.
  151.      */
  152.     public static CvMappingValidator of(URL mappingFile,
  153.         boolean errorIfTermNotInRule) throws JAXBException {
  154.         OLSWsConfig config = new OLSWsConfig();
  155.         OLSClient client = new OLSClient(config);
  156.         CvParameterLookupService service = new CvParameterLookupService(client);
  157.         return of(mappingFile, service, errorIfTermNotInRule);
  158.     }

  159.     /**
  160.      * Create a new instance of CvMappingValidator.
  161.      *
  162.      * Uses the provided {@link CvParameterLookupService}.
  163.      *
  164.      * @param mappingFile the mapping file URL to use
  165.      * @param client the ontology lookup service client
  166.      * @param errorIfTermNotInRule raise an error if a term is not defined within an otherwise matching rule for the element
  167.      * @return a new CvMappingValidator instance
  168.      * @throws JAXBException if errors occur during unmarshalling of the mapping xml file.
  169.      */
  170.     public static CvMappingValidator of(URL mappingFile,
  171.         CvParameterLookupService client, boolean errorIfTermNotInRule) throws JAXBException {
  172.         JAXBContext jaxbContext = JAXBContext.newInstance(CvMapping.class);
  173.         Unmarshaller u = jaxbContext.createUnmarshaller();
  174.         CvMapping mapping = (CvMapping) u.unmarshal(mappingFile);
  175.         return new CvMappingValidator.CvMappingValidatorBuilder().mapping(
  176.             mapping).
  177.             ruleHandler(new ResolvingCvRuleHandler(client)).
  178.             errorIfTermNotInRule(errorIfTermNotInRule).
  179.             andHandler(new AndValidationHandler()).
  180.             orHandler(new OrValidationHandler()).
  181.             xorHandler(new XorValidationHandler()).
  182.             extraHandler(new ExtraParametersValidationHandler()).
  183.             sharedHandler(new SharedParametersValidationHandler()).
  184.             cvTermSelectionHandler(new RemoveUserParams()).
  185.             emptyRuleHandler(new EmptyRuleHandler()).
  186.             build().
  187.             withPreValidator(new CvDefinitionValidationHandler());
  188.     }

  189.     /**
  190.      * Create a new instance of CvMappingValidator.
  191.      *
  192.      * Uses the provided {@link CvParameterLookupService}.
  193.      *
  194.      * @param mapping the cv mapping to use
  195.      * @param client the ontology lookup service client
  196.      * @param errorIfTermNotInRule raise an error if a term is not defined within an otherwise matching rule for the element
  197.      * @return a new CvMappingValidator instance
  198.      */
  199.     public static CvMappingValidator of(CvMapping mapping,
  200.         CvParameterLookupService client,
  201.         boolean errorIfTermNotInRule) {
  202.         return new CvMappingValidator.CvMappingValidatorBuilder().mapping(
  203.             mapping).
  204.             ruleHandler(new ResolvingCvRuleHandler(client)).
  205.             errorIfTermNotInRule(errorIfTermNotInRule).
  206.             andHandler(new AndValidationHandler()).
  207.             orHandler(new OrValidationHandler()).
  208.             xorHandler(new XorValidationHandler()).
  209.             extraHandler(new ExtraParametersValidationHandler()).
  210.             sharedHandler(new SharedParametersValidationHandler()).
  211.             cvTermSelectionHandler(new RemoveUserParams()).
  212.             emptyRuleHandler(new EmptyRuleHandler()).
  213.             build().
  214.             withPreValidator(new CvDefinitionValidationHandler());
  215.     }

  216.     @Override
  217.     public List<ValidationMessage> validate(MzTab mzTab) {
  218.         final List<ValidationMessage> messages = new LinkedList<>();
  219.         log.debug("Applying {} pre validation steps.", preValidators.size());
  220.         preValidators.stream().
  221.             forEach((validator) ->
  222.             {
  223.                 messages.addAll(validator.validate(mzTab));
  224.             });
  225.         messages.addAll(new CvDefinitionValidationHandler().validate(mzTab));
  226.         JXPathContext context = JXPathContext.newContext(mzTab);
  227.         log.debug("Applying {} cv rule mapping steps.", mapping.
  228.             getCvMappingRuleList().
  229.             getCvMappingRule().
  230.             size());
  231.         mapping.getCvMappingRuleList().
  232.             getCvMappingRule().
  233.             forEach((rule) ->
  234.             {
  235.                 messages.addAll(handleRule(context, rule, errorIfTermNotInRule));
  236.             });
  237.         log.debug("Applying {} post validation steps.", preValidators.size());
  238.         postValidators.stream().
  239.             forEach((validator) ->
  240.             {
  241.                 messages.addAll(validator.validate(mzTab));
  242.             });
  243.         return messages;
  244.     }

  245.     private List<ValidationMessage> handleRule(JXPathContext context,
  246.         CvMappingRule rule, boolean errorOnTermNotInRule) {
  247.         String path = rule.getCvElementPath();
  248.         List<Pair<Pointer, Parameter>> selection = JxPathElement.
  249.             toList(context, path, Parameter.class);

  250.         final List<ValidationMessage> messages = emptyRuleHandler.handleRule(
  251.             rule, selection);
  252.         if (!messages.isEmpty()) {
  253.             return messages;
  254.         }

  255.         final List<Pair<Pointer, Parameter>> filteredSelection = cvTermSelectionHandler.
  256.             handleSelection(selection);

  257.         // and logic means that ALL of the defined terms or their children MUST appear
  258.         // we only compare valid CVParameters here, user Params (no cv accession), are not compared!
  259.         // if combination logic is AND, child expansion needs to be disabled to avoid nonsensical combinations
  260.         try {
  261.             RuleEvaluationResult result = ruleHandler.handleRule(rule,
  262.                 filteredSelection);

  263.             switch (rule.getCvTermsCombinationLogic()) {
  264.                 case AND:
  265.                     messages.addAll(andHandler.handleParameters(result,
  266.                         errorOnTermNotInRule));
  267.                     break;
  268.                 case OR: // any of the terms or their children need to appear
  269.                     messages.addAll(orHandler.handleParameters(result,
  270.                         errorOnTermNotInRule));
  271.                     break;
  272.                 case XOR:
  273.                     messages.addAll(xorHandler.handleParameters(result,
  274.                         errorOnTermNotInRule));
  275.                     break;
  276.                 default:
  277.                     throw new IllegalArgumentException(
  278.                         "Unknown combination logic value: " + rule.
  279.                             getCvTermsCombinationLogic() + " on rule " + CvMappingUtils.
  280.                             niceToString(rule) + "! Supported are: " + Arrays.
  281.                         toString(CvMappingRule.CvTermsCombinationLogic.
  282.                             values()));
  283.             }
  284.             //        messages.addAll(sharedHandler.handleParameters(result, errorOnTermNotInRule));
  285.             messages.addAll(extraHandler.handleParameters(result,
  286.                 errorOnTermNotInRule));
  287.         } catch (RuntimeException re) {
  288.             log.error(
  289.                 "Caught exception while running semantic validation on rule " + CvMappingUtils.
  290.                     niceToString(rule) + " with selection " + filteredSelection,
  291.                 re);
  292.             MZTabError error = new MZTabError(
  293.                 CrossCheckErrorType.SemanticValidationException, -1, re.
  294.                     getMessage());
  295.             messages.add(error.toValidationMessage());
  296.         }
  297.         return messages.isEmpty() ? Collections.emptyList() : messages;
  298.     }

  299. }