001/*
002 * Copyright 2020  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.palinom.goslinfragments;
017
018import de.isas.lipidomics.domain.Adduct;
019import de.isas.lipidomics.domain.Fragment;
020import de.isas.lipidomics.domain.LipidAdduct;
021import de.isas.lipidomics.domain.LipidCategory;
022import de.isas.lipidomics.domain.LipidSpecies;
023import de.isas.lipidomics.palinom.GoslinFragmentsBaseVisitor;
024import de.isas.lipidomics.palinom.GoslinFragmentsParser;
025import de.isas.lipidomics.palinom.exceptions.ParseTreeVisitorException;
026import java.util.Arrays;
027import java.util.BitSet;
028import java.util.Optional;
029import lombok.extern.slf4j.Slf4j;
030
031/**
032 * Base visitor implementation for the GoslinFragments grammar.
033 *
034 * Overriding implementation of {@link GoslinFragmentsBaseVisitor}. Creates
035 * {@link LipidAdduct} instances from the provided context.
036 *
037 * @author nils.hoffmann
038 */
039@Slf4j
040public class GoslinFragmentsVisitorImpl extends GoslinFragmentsBaseVisitor<LipidAdduct> {
041
042    /**
043     * Produces a LipidAdduct given the LipidContext.
044     *
045     * @throws ParseTreeVisitorException for structural or state-related issues
046     * while trying to process a parsing context.
047     * @throws RuntimeException
048     * @param ctx
049     * @return a LipidAdduct.
050     */
051    @Override
052    public LipidAdduct visitLipid(GoslinFragmentsParser.LipidContext ctx) {
053        GoslinFragmentsParser.Lipid_eofContext lipid = ctx.lipid_eof();
054        Optional<GoslinFragmentsParser.Fragment_nameContext> fragmentContext = Optional.ofNullable(lipid.fragment_name());
055        Optional<GoslinFragmentsParser.Lipid_pureContext> lipidContext = Optional.ofNullable(lipid.just_lipid().lipid_pure());
056        Optional<GoslinFragmentsParser.Adduct_infoContext> adductTermContext = Optional.ofNullable(lipid.just_lipid().adduct_info());
057        LipidAdduct la = new LipidAdduct(lipidContext.map((cc) -> {
058            return new GoslinFragmentsVisitorImpl.LipidVisitor().visitLipid_pure(cc);
059        }).orElse(LipidSpecies.NONE), adductTermContext.map((t) -> {
060            return new GoslinFragmentsVisitorImpl.AdductVisitor().visitAdduct_info(t);
061        }).orElse(Adduct.NONE), fragmentContext.map((t) -> {
062            return new Fragment(t.frag_char().getText());
063        }).orElse(Fragment.NONE));
064        return la;
065    }
066
067    private static class LipidVisitor extends GoslinFragmentsBaseVisitor<LipidSpecies> {
068
069        @Override
070        public LipidSpecies visitLipid_pure(GoslinFragmentsParser.Lipid_pureContext ctx) {
071            LipidSpecies lipid = null;
072            BitSet bs = new BitSet(5);
073            bs.set(LipidCategory.ST.ordinal(), ctx.sterol() != null);
074            bs.set(LipidCategory.GL.ordinal(), ctx.gl() != null);
075            bs.set(LipidCategory.FA.ordinal(), ctx.mediatorc() != null);
076            bs.set(LipidCategory.GP.ordinal(), ctx.pl() != null);
077            bs.set(LipidCategory.SP.ordinal(), ctx.sl() != null);
078            final FattyAcylHelper faHelper = new FattyAcylHelper();
079            final MolecularSubspeciesFasHandler msfh = new MolecularSubspeciesFasHandler(faHelper);
080            final IsomericSubspeciesFasHandler isfh = new IsomericSubspeciesFasHandler(faHelper);
081            final StructuralSubspeciesFasHandler ssfh = new StructuralSubspeciesFasHandler(isfh, faHelper);
082            final IsomericSubspeciesLcbHandler islh = new IsomericSubspeciesLcbHandler(isfh, faHelper);
083            final StructuralSubspeciesLcbHandler sslh = new StructuralSubspeciesLcbHandler(ssfh, islh);
084            final FattyAcylHandler faHandler = new FattyAcylHandler();
085            LipidCategory contextCategory = LipidCategory.UNDEFINED;
086            switch (bs.cardinality()) {
087                case 0:
088                    throw new ParseTreeVisitorException("Parsing context did not contain content for any lipid category. Must contain exactly one of " + Arrays.toString(LipidCategory.values()));
089                case 1:
090                    contextCategory = LipidCategory.values()[bs.nextSetBit(0)];
091                    break;
092                default:
093                    throw new ParseTreeVisitorException("Parsing context contained content for more than one lipid category. Must contain exactly one of " + Arrays.toString(LipidCategory.values()));
094            }
095            switch (contextCategory) {
096                case ST:
097                    lipid = new SterolLipidHandler(
098                            ssfh
099                    ).handle(ctx);
100                    break;
101                case GL:
102                    lipid = new GlyceroLipidHandler(
103                            msfh,
104                            ssfh,
105                            faHandler
106                    ).handle(ctx);
107                    break;
108                case FA:
109                    lipid = new FattyAcylHandler().handle(ctx);
110                    break;
111                case GP:
112                    lipid = new GlycerophosphoLipidHandler(
113                            msfh,
114                            ssfh,
115                            faHandler
116                    ).handle(ctx);
117                    break;
118                case SP:
119                    lipid = new SphingoLipidHandler(
120                            sslh,
121                            faHandler
122                    ).handle(ctx);
123                    break;
124                default:
125                    throw new ParseTreeVisitorException("Unhandled contextCategory: " + contextCategory);
126            }
127            return lipid;
128        }
129
130    }
131
132    private static class AdductVisitor extends GoslinFragmentsBaseVisitor<Adduct> {
133
134        @Override
135        public Adduct visitAdduct_info(GoslinFragmentsParser.Adduct_infoContext ctx) {
136            String chargeSign = ctx.charge_sign().getText();
137            Integer chargeSignValue = 0;
138            switch (chargeSign) {
139                case "+":
140                    chargeSignValue = 1;
141                    break;
142                case "-":
143                    chargeSignValue = -1;
144                    break;
145                default:
146                    chargeSignValue = 0;
147            }
148            String adductText = ctx.adduct().getText();
149            Adduct adduct = new Adduct("", adductText, Integer.parseInt(ctx.charge().getText()), chargeSignValue);
150            return adduct;
151        }
152    }
153}