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 = Optional.of(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
081    @Override
082    public Map<String, FattyAcid> getFa() {
083        return Collections.unmodifiableMap(fa);
084    }
085
086    protected String getHeadGroupSuffix() {
087        String hgToFaSep = " ";
088        if (isEtherLipid()) {
089            hgToFaSep = " O-";
090        }
091        return hgToFaSep;
092    }
093
094    protected StringBuilder buildSubspeciesHeadGroupString(String headGroup, boolean normalizeHeadGroup) {
095        StringBuilder lipidString = new StringBuilder();
096        lipidString.append(getHeadGroup().getLipidClass().map((lclass) -> {
097            switch (lclass) {
098//                case SE:
099                case SE_27_1:
100                case SE_27_2:
101                case SE_28_2:
102                case SE_28_3:
103                case SE_29_2:
104                case SE_30_2:
105                    if (!getFa().isEmpty()) {
106                        return (normalizeHeadGroup ? headGroup : getNormalizedHeadGroup()) + getHeadGroupSuffix().trim() + "/";
107                    }
108            }
109            return headGroup + (getFa().isEmpty() ? "" : getHeadGroupSuffix());
110        }).orElse(headGroup + (getFa().isEmpty() ? "" : getHeadGroupSuffix())));
111        return lipidString;
112    }
113
114    protected String buildLipidSubspeciesName(LipidLevel level, String faSeparator, String headGroup, boolean isNormalized) {
115        StringBuilder sb = new StringBuilder();
116        String faStrings = getFa().values().stream().map((fa) -> {
117            return fa.buildSubstructureName(level);
118        }).collect(Collectors.joining(faSeparator));
119        return sb.append(buildSubspeciesHeadGroupString(headGroup, isNormalized)
120        ).append(faStrings).toString();
121    }
122
123    @Override
124    public String getLipidString(LipidLevel level) {
125        return this.getLipidString(level, false);
126    }
127
128    @Override
129    public String getLipidString(LipidLevel level, boolean normalizeHeadGroup) {
130        String headGroup = normalizeHeadGroup ? getNormalizedHeadGroup() : getHeadGroup().getName();
131        switch (level) {
132            case MOLECULAR_SUBSPECIES:
133                return buildLipidSubspeciesName(level, "-", headGroup, normalizeHeadGroup);
134            case CATEGORY:
135            case CLASS:
136            case SPECIES:
137                return super.getLipidString(level, normalizeHeadGroup);
138            default:
139                LipidLevel thisLevel = getInfo().orElse(LipidSpeciesInfo.NONE).getLevel();
140                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!");
141        }
142    }
143
144    @Override
145    public String getNormalizedLipidString() {
146        return getLipidString(getInfo().orElse(LipidSpeciesInfo.NONE).getLevel(), true);
147    }
148
149    @Override
150    public boolean validate() {
151        return true;
152    }
153
154    @Override
155    public String toString() {
156        return getLipidString();
157    }
158
159}