001/*
002 * Copyright 2018 Leibniz-Institut für Analytische Wissenschaften – ISAS – e.V..
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package de.isas.mztab2.validation;
017
018import de.isas.lipidomics.mztab2.validation.Validator;
019import de.isas.mztab2.cvmapping.JxPathElement;
020import de.isas.mztab2.cvmapping.SetOperations;
021import de.isas.mztab2.io.serialization.ParameterConverter;
022import de.isas.mztab2.model.CV;
023import de.isas.mztab2.model.MzTab;
024import de.isas.mztab2.model.Parameter;
025import de.isas.mztab2.model.ValidationMessage;
026import java.util.ArrayList;
027import java.util.HashSet;
028import java.util.List;
029import java.util.Map;
030import java.util.Set;
031import java.util.stream.Collectors;
032import lombok.extern.slf4j.Slf4j;
033import org.apache.commons.jxpath.JXPathContext;
034import org.apache.commons.jxpath.Pointer;
035import org.apache.commons.lang3.tuple.Pair;
036import uk.ac.ebi.pride.jmztab2.utils.errors.CrossCheckErrorType;
037import uk.ac.ebi.pride.jmztab2.utils.errors.MZTabError;
038
039/**
040 * Validates that controlled vocabularies used by parameters are defined in the
041 * metadata CV section.
042 *
043 * @author nilshoffmann
044 */
045@Slf4j
046@lombok.Builder()
047public class CvDefinitionValidationHandler implements Validator<MzTab> {
048
049    @Override
050    public List<ValidationMessage> validate(MzTab mzTab) {
051        JXPathContext context = JXPathContext.newContext(mzTab);
052        return checkCvDefinitions(mzTab, context);
053    }
054
055    private List<ValidationMessage> checkCvDefinitions(MzTab mzTabFile,
056        JXPathContext context) {
057        Map<String, CV> cvTerms = mzTabFile.getMetadata().
058            getCv().
059            stream().
060            collect(Collectors.toMap((key) ->
061            {
062                return key.getLabel();
063            }, (value) ->
064            {
065                return value;
066            }));
067        List<ValidationMessage> messages = new ArrayList<>();
068        List<Pair<Pointer, Parameter>> parameters = JxPathElement.
069            toList(context, "//*[cvLabel!='']",
070                Parameter.class);
071        log.debug("Selected {} cv parameters!", parameters.size());
072        Set<String> definedCvLabels = new HashSet<>(cvTerms.keySet());
073        Set<String> usedCvLabels = new HashSet<>();
074        parameters.stream().
075            forEach((t) ->
076            {
077                Parameter param = t.getValue();
078                log.debug("Checking parameter {}", new ParameterConverter().
079                    convert(param));
080                if (param.getCvLabel() != null && !param.getCvLabel().
081                    isEmpty()) {
082                    usedCvLabels.add(param.getCvLabel());
083                    if (!cvTerms.containsKey(param.getCvLabel())) {
084                        log.debug(
085                            "Parameter {} uses undefined controlled vocabulary: {}",
086                            new ParameterConverter().
087                                convert(param), param.getCvLabel());
088                        MZTabError error = new MZTabError(
089                            CrossCheckErrorType.CvUndefinedInMetadata, -1,
090                            param.getCvLabel(),
091                            new ParameterConverter().convert(param));
092                        messages.add(error.toValidationMessage());
093                    }
094                }
095            });
096        Set<String> unusedCvLabels = SetOperations.complement(definedCvLabels,
097            usedCvLabels);
098        unusedCvLabels.stream().
099            forEach((cvLabel) ->
100            {
101                log.debug(
102                    "Cv with label {} is not being used by any parameters!",
103                    cvLabel);
104                MZTabError error = new MZTabError(
105                    CrossCheckErrorType.CvUnused, -1,
106                    cvLabel);
107                messages.add(error.toValidationMessage());
108            });
109        return messages;
110    }
111}