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.palinom.goslin;
017
018import de.isas.lipidomics.domain.Adduct;
019import de.isas.lipidomics.domain.Fragment;
020import de.isas.lipidomics.domain.LipidSpecies;
021import de.isas.lipidomics.domain.LipidAdduct;
022import de.isas.lipidomics.domain.LipidCategory;
023import de.isas.lipidomics.palinom.GoslinBaseVisitor;
024import de.isas.lipidomics.palinom.GoslinParser;
025import de.isas.lipidomics.palinom.GoslinParser.Adduct_infoContext;
026import de.isas.lipidomics.palinom.GoslinParser.Lipid_pureContext;
027import de.isas.lipidomics.palinom.exceptions.ParseTreeVisitorException;
028import java.util.Arrays;
029import java.util.BitSet;
030import java.util.Optional;
031import lombok.extern.slf4j.Slf4j;
032
033/**
034 * Base visitor implementation for the Goslin grammar.
035 * 
036 *
037 * Overriding implementation of {@link GoslinBaseVisitor}. Creates
038 * {@link LipidAdduct} instances from the provided context.
039 * 
040 * @author nils.hoffmann
041 */
042@Slf4j
043public class GoslinVisitorImpl extends GoslinBaseVisitor<LipidAdduct> {
044
045    /**
046     * Produces a LipidAdduct given the LipidContext.
047     * @throws ParseTreeVisitorException for structural or state-related issues
048     * while trying to process a parsing context.
049     * @throws RuntimeException
050     * @param ctx
051     * @return a LipidAdduct.
052     */
053    @Override
054    public LipidAdduct visitLipid(GoslinParser.LipidContext ctx) {
055        GoslinParser.Lipid_eofContext lipid = ctx.lipid_eof();
056        Optional<Lipid_pureContext> categoryContext = Optional.ofNullable(lipid.lipid_pure());
057        Optional<Adduct_infoContext> adductTermContext = Optional.ofNullable(lipid.adduct_info());
058
059        LipidAdduct la = new LipidAdduct(categoryContext.map((cc) -> {
060            return new LipidVisitor().visitLipid_pure(cc);
061        }).orElse(LipidSpecies.NONE), adductTermContext.map((t) -> {
062            return new AdductVisitor().visitAdduct_info(t);
063        }).orElse(Adduct.NONE), new Fragment(""));
064        return la;
065    }
066
067    private static class LipidVisitor extends GoslinBaseVisitor<LipidSpecies> {
068
069        @Override
070        public LipidSpecies visitLipid_pure(GoslinParser.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 GoslinBaseVisitor<Adduct> {
133
134        @Override
135        public Adduct visitAdduct_info(GoslinParser.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}