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 uk.ac.ebi.pride.jmztab2.utils.parser;
017
018import de.isas.mztab2.model.Assay;
019import de.isas.mztab2.model.Metadata;
020import de.isas.mztab2.model.Parameter;
021import java.util.*;
022import org.slf4j.Logger;
023import org.slf4j.LoggerFactory;
024import uk.ac.ebi.pride.jmztab2.model.IMZTabColumn;
025import uk.ac.ebi.pride.jmztab2.model.ISmallMoleculeFeatureColumn;
026import uk.ac.ebi.pride.jmztab2.model.MZTabColumnFactory;
027import uk.ac.ebi.pride.jmztab2.model.MZTabConstants;
028import uk.ac.ebi.pride.jmztab2.model.Section;
029import uk.ac.ebi.pride.jmztab2.model.SmallMoleculeFeatureColumn;
030import uk.ac.ebi.pride.jmztab2.utils.errors.FormatErrorType;
031import uk.ac.ebi.pride.jmztab2.utils.errors.LogicalErrorType;
032import uk.ac.ebi.pride.jmztab2.utils.errors.MZTabError;
033import uk.ac.ebi.pride.jmztab2.utils.errors.MZTabErrorList;
034import uk.ac.ebi.pride.jmztab2.utils.errors.MZTabException;
035
036/**
037 * Parse and validate Small Molecule Feature header line into a {@link uk.ac.ebi.pride.jmztab2.model.MZTabColumnFactory}.
038 *
039 * @author nilshoffmann
040 * @since 11/09/17
041 * 
042 */
043public class SFHLineParser extends MZTabHeaderLineParser {
044
045    private static final Logger LOGGER = LoggerFactory.getLogger(SFHLineParser.class);
046    private Map<Integer, String> physPositionToOrder;
047
048
049    /**
050     * <p>Constructor for SFHLineParser.</p>
051     *
052     * @param context a {@link uk.ac.ebi.pride.jmztab2.utils.parser.MZTabParserContext} object.
053     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
054     */
055    public SFHLineParser(MZTabParserContext context, Metadata metadata) {
056        super(context, MZTabColumnFactory.getInstance(Section.Small_Molecule_Feature_Header), metadata);
057    }
058
059    /** {@inheritDoc} */
060    @Override
061    protected int parseColumns() throws MZTabException {
062        String header;
063        Integer physicalPosition;
064
065        ISmallMoleculeFeatureColumn column;
066        SortedMap<String, IMZTabColumn> columnMapping = factory.getColumnMapping();
067        SortedMap<String, IMZTabColumn> optionalMapping = factory.getOptionalColumnMapping();
068        SortedMap<String, IMZTabColumn> stableMapping = factory.getStableColumnMapping();
069
070        physPositionToOrder = generateHeaderPhysPositionToOrderMap(items);
071
072        //Iterates through the tokens in the protein header
073        //It will identify the type of column and the position accordingly
074        for (physicalPosition = 1; physicalPosition < items.length; physicalPosition++) {
075
076            column = null;
077            header = items[physicalPosition];
078
079            if (header.contains(MZTabConstants.ABUNDANCE_PREFIX)) {
080                checkAbundanceColumns(physicalPosition, physPositionToOrder.get(physicalPosition));
081            } else if (header.startsWith(MZTabConstants.OPT_PREFIX)) {
082                checkOptColumnName(header);
083            } else {
084                try {
085                    column = SmallMoleculeFeatureColumn.Stable.columnFor(header);
086                } catch(IllegalArgumentException ex) {
087                    throw new MZTabException(new MZTabError(LogicalErrorType.ColumnNotValid,lineNumber,header,section.getName()));
088                }
089                
090            }
091
092            if (column != null) {
093                if (!column.getOrder().equals(physPositionToOrder.get(physicalPosition))) {
094                    column.setOrder(physPositionToOrder.get(physicalPosition));
095                    LOGGER.debug(column.toString());
096                }
097                if(column.isOptional()){
098                    optionalMapping.put(column.getLogicPosition(), column);
099                } else {
100                    stableMapping.put(column.getLogicPosition(), column);
101                }
102                columnMapping.put(column.getLogicPosition(), column);
103            }
104        }
105        return physicalPosition;
106    }
107
108    private Map<Integer, String> generateHeaderPhysPositionToOrderMap(String[] items) {
109        Integer physicalPosition;
110        Map<Integer, String> physicalPositionToOrder = new LinkedHashMap<>();
111        int order = 0;
112
113        for (physicalPosition = 1; physicalPosition < items.length; physicalPosition++) {
114            if(physicalPositionToOrder.containsKey(physicalPosition)) {
115                throw new IllegalArgumentException("Physical position "+physicalPosition+" for item "+items[physicalPosition-1]+" is already assigned!");
116            }
117            physicalPositionToOrder.put(physicalPosition, fromIndexToOrder(++order));
118        }
119        return physicalPositionToOrder;
120    }
121
122    /**
123     * {@inheritDoc}
124     *
125     * The following optional columns are mandatory:
126     * 1. abundance_assay[1-n]
127     *
128     * NOTICE: this method will be called at end of parse() function.
129     * @see MZTabHeaderLineParser#parse(int, String, MZTabErrorList)
130     * @see MZTabHeaderLineParser#parse(int, String, MZTabErrorList)
131     */
132    @Override
133    protected void refine() throws MZTabException {
134
135        //mandatory columns
136        List<String> mandatoryColumnHeaders = new ArrayList<>();
137        for(ISmallMoleculeFeatureColumn column: SmallMoleculeFeatureColumn.Stable.columns()) {
138            mandatoryColumnHeaders.add(column.getName());
139        }
140
141        Parameter smallMoleculeFeatureQuantificationUnit = Optional.ofNullable(metadata.getSmallMoleculeFeatureQuantificationUnit()).orElseThrow(() -> 
142            new MZTabException(new MZTabError(LogicalErrorType.NoSmallMoleculeFeatureQuantificationUnit, lineNumber)));
143
144        for (String columnHeader : mandatoryColumnHeaders) {
145            if (factory.findColumnByHeader(columnHeader) == null) {
146                throw new MZTabException(new MZTabError(FormatErrorType.StableColumn, lineNumber, columnHeader));
147            }
148        }
149
150        for (Assay assay : metadata.getAssay()) {
151            String assayLabel = "_"+Metadata.Properties.assay+"[" + assay.getId() + "]";
152            refineOptionalColumn(Section.Small_Molecule_Feature_Header, "abundance" + assayLabel);
153        }
154    }
155}