001/*
002 * Copyright 2019 nils.hoffmann.
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.lipidomics.domain;
017
018import de.isas.lipidomics.palinom.exceptions.ConstraintViolationException;
019import java.util.Collections;
020import java.util.LinkedHashMap;
021import java.util.Map;
022import java.util.Optional;
023import java.util.stream.Collectors;
024import lombok.Builder;
025import lombok.Data;
026import lombok.EqualsAndHashCode;
027import lombok.extern.slf4j.Slf4j;
028
029/**
030 * A molecular subspecies. Child of LipidSpecies. Individual FAs are known, but
031 * neither their sn positions nor double bond positions.
032 *
033 * Example: Phosphatidylinositol (8:0-8:0) or PI(8:0-8:0)
034 *
035 * @author nils.hoffmann
036 * @see LipidSpecies
037 */
038@Slf4j
039@Data
040@EqualsAndHashCode(callSuper = true)
041public class LipidMolecularSubspecies extends LipidSpecies {
042
043    protected final Map<String, FattyAcid> fa = new LinkedHashMap<>();
044
045    @Builder
046    public LipidMolecularSubspecies(HeadGroup headGroup, FattyAcid... fa) {
047        super(headGroup);
048        int nCarbon = 0;
049        int nHydroxyl = 0;
050        int nDoubleBonds = 0;
051        ModificationsList mods = new ModificationsList();
052        for (FattyAcid fas : fa) {
053//            if (fas.getPosition() != -1) {
054//                throw new ConstraintViolationException("MolecularFattyAcid " + fas.getName() + " must have position set to -1! Was: " + fas.getPosition());
055//            }
056            if (this.fa.containsKey(fas.getName())) {
057                throw new ConstraintViolationException(
058                        "FA names must be unique! FA with name " + fas.getName() + " was already added!");
059            } else {
060                this.fa.put(fas.getName(), fas);
061                nCarbon += fas.getNCarbon();
062                nHydroxyl += fas.getNHydroxy();
063                nDoubleBonds += fas.getNDoubleBonds();
064                mods.addAll(fas.getModifications());
065            }
066
067        }
068        super.info = LipidSpeciesInfo.lipidSpeciesInfoBuilder().
069                level(LipidLevel.MOLECULAR_SUBSPECIES).
070                name(headGroup.getName()).
071                position(-1).
072                nCarbon(nCarbon).
073                nHydroxy(nHydroxyl).
074                nDoubleBonds(nDoubleBonds).
075                lipidFaBondType(LipidFaBondType.getLipidFaBondType(headGroup, fa)).
076                modifications(mods).
077                build();
078    }
079
080    @Override
081    public Map<String, FattyAcid> getFa() {
082        return Collections.unmodifiableMap(fa);
083    }
084
085    protected String getHeadGroupSuffix() {
086        String hgToFaSep = " ";
087        if (isEtherLipid()) {
088            hgToFaSep = " O-";
089        }
090        return hgToFaSep;
091    }
092
093    protected StringBuilder buildSubspeciesHeadGroupString(String headGroup, boolean normalizeHeadGroup) {
094        StringBuilder lipidString = new StringBuilder();
095        lipidString.append(Optional.ofNullable(getHeadGroup().getLipidClass()).map((lclass) -> {
096            switch (lclass) {
097//                case SE:
098                case SE_27_1:
099                case SE_27_2:
100                case SE_28_2:
101                case SE_28_3:
102                case SE_29_2:
103                case SE_30_2:
104                    if (!getFa().isEmpty()) {
105                        return (normalizeHeadGroup ? headGroup : getNormalizedHeadGroup()) + getHeadGroupSuffix().trim() + "/";
106                    }
107            }
108            return headGroup + (getFa().isEmpty() ? "" : getHeadGroupSuffix());
109        }).orElse(headGroup + (getFa().isEmpty() ? "" : getHeadGroupSuffix())));
110        return lipidString;
111    }
112
113    protected String buildLipidSubspeciesName(LipidLevel level, String faSeparator, String headGroup, boolean isNormalized) {
114        StringBuilder sb = new StringBuilder();
115        String faStrings = getFa().values().stream().map((fa) -> {
116            return fa.buildSubstructureName(level);
117        }).collect(Collectors.joining(faSeparator));
118        return sb.append(buildSubspeciesHeadGroupString(headGroup, isNormalized)
119        ).append(faStrings).toString();
120    }
121
122    @Override
123    public String getLipidString(LipidLevel level) {
124        return this.getLipidString(level, false);
125    }
126
127    @Override
128    public String getLipidString(LipidLevel level, boolean normalizeHeadGroup) {
129        String headGroup = normalizeHeadGroup ? getNormalizedHeadGroup() : getHeadGroup().getName();
130        switch (level) {
131            case MOLECULAR_SUBSPECIES:
132                return buildLipidSubspeciesName(level, "-", headGroup, normalizeHeadGroup);
133            case CATEGORY:
134            case CLASS:
135            case SPECIES:
136                return super.getLipidString(level, normalizeHeadGroup);
137            default:
138                LipidLevel thisLevel = getInfo().getLevel();
139                throw new ConstraintViolationException(getClass().getSimpleName() + " can not create a string for lipid with level " + thisLevel + " for level " + level + ": target level is more specific than this lipid's level!");
140        }
141    }
142
143    @Override
144    public String getNormalizedLipidString() {
145        return getLipidString(getInfo().getLevel(), true);
146    }
147
148    @Override
149    public boolean validate() {
150        return true;
151    }
152
153    @Override
154    public String toString() {
155        return getLipidString();
156    }
157
158}