001/*
002 * Copyright (c) 2024 QOS.ch Sarl (Switzerland)
003 * All rights reserved.
004 *
005 * Permission is hereby granted, free  of charge, to any person obtaining
006 * a  copy  of this  software  and  associated  documentation files  (the
007 * "Software"), to  deal in  the Software without  restriction, including
008 * without limitation  the rights to  use, copy, modify,  merge, publish,
009 * distribute,  sublicense, and/or sell  copies of  the Software,  and to
010 * permit persons to whom the Software  is furnished to do so, subject to
011 * the following conditions:
012 *
013 * The  above  copyright  notice  and  this permission  notice  shall  be
014 * included in all copies or substantial portions of the Software.
015 *
016 * THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
017 * EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
018 * MERCHANTABILITY,    FITNESS    FOR    A   PARTICULAR    PURPOSE    AND
019 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
020 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
021 * OF CONTRACT, TORT OR OTHERWISE,  ARISING FROM, OUT OF OR IN CONNECTION
022 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
023 *
024 *
025 *
026 */
027
028package ch.qos.logback.tyler.base.handler;
029
030import ch.qos.logback.core.Context;
031import ch.qos.logback.core.model.Model;
032import ch.qos.logback.core.model.conditional.IfModel;
033import ch.qos.logback.core.model.processor.ModelHandlerBase;
034import ch.qos.logback.core.model.processor.ModelHandlerException;
035import ch.qos.logback.core.model.processor.ModelInterpretationContext;
036import ch.qos.logback.core.util.OptionHelper;
037import ch.qos.logback.tyler.base.TylerModelInterpretationContext;
038
039/**
040 * Handler responsible for processing IfModel instances encountered during model interpretation.
041 * <p>
042 * This handler pushes the IfModel onto the interpretation stack, validates the condition
043 * string and emits the corresponding Java "if" control flow into the Tyler configuration
044 * method being built (via MethodSpec).
045 * </p>
046 */
047public class IfModelHandler extends ModelHandlerBase {
048
049    /**
050     * Create a new IfModelHandler.
051     *
052     * @param context the Logback context used for this handler
053     */
054    public IfModelHandler(Context context) {
055        super(context);
056    }
057
058    /**
059     * Factory method used by the model processing infrastructure.
060     *
061     * @param context the Logback context
062     * @param ic the current ModelInterpretationContext (unused by this factory)
063     * @return a new instance of IfModelHandler
064     */
065    static public ModelHandlerBase makeInstance(Context context, ModelInterpretationContext ic) {
066        return new IfModelHandler(context);
067    }
068
069    /**
070     * Return the model class that this handler supports.
071     *
072     * @return the supported model class (IfModel.class)
073     */
074    @Override
075    protected Class<IfModel> getSupportedModelClass() {
076        return IfModel.class;
077    }
078
079    /**
080     * Handle an {@link IfModel} by validating its condition and emitting the
081     * corresponding Java "if" statement into the Tyler method spec builder.
082     *
083     * @param mic the model interpretation context
084     * @param model the model to handle (expected to be {@link IfModel})
085     * @throws ModelHandlerException if an error occurs while handling the model
086     */
087    @Override
088    public void handle(ModelInterpretationContext mic, Model model) throws ModelHandlerException {
089        IfModel ifModel = (IfModel) model;
090        TylerModelInterpretationContext tmic = (TylerModelInterpretationContext) mic;
091
092
093        Object o = tmic.peekObject();
094        String conditionStr = null;
095
096        if(o instanceof ConditionStringRecord conditionStringRecord) {
097            conditionStr = conditionStringRecord.value();
098            mic.popObject();
099        }
100
101        mic.pushModel(ifModel);
102        if(conditionStr ==  null) {
103            conditionStr = ifModel.getCondition();
104        }
105
106        int lineNum = model.getLineNumber();
107
108        if (!OptionHelper.isNullOrEmptyOrAllSpaces(conditionStr)) {
109            addJavaStatement(tmic, conditionStr);
110        } else {
111            addError("Empty condition for <if> element on line "+lineNum);
112        }
113    }
114
115    /**
116     * Emit the Java "if" control flow for the given condition into the supplied
117     * TylerModelInterpretationContext's MethodSpec builder.
118     *
119     * @param tmic the Tyler model interpretation context containing the MethodSpec builder
120     * @param conditionStr the Java expression to use as the if condition
121     */
122    protected void addJavaStatement(TylerModelInterpretationContext tmic, String conditionStr) {
123        tmic.configureMethodSpecBuilder.beginControlFlow("if($N)", conditionStr);
124    }
125
126    /**
127     * Finalize handling of the {@link IfModel}: verify the model stack, end the
128     * emitted control flow and pop the model from the stack.
129     *
130     * @param mic the model interpretation context
131     * @param model the model that was handled
132     */
133    @Override
134    public void postHandle(ModelInterpretationContext mic, Model model) {
135        if(mic.isModelStackEmpty()) {
136            addError("Unexpected unexpected empty model stack.");
137            return;
138        }
139
140        Object o = mic.peekModel();
141        if (!(o instanceof IfModel)) {
142            addWarn("The object [" + o + "] on the top the of the stack is not of type [" + IfModel.class);
143        } else {
144            TylerModelInterpretationContext tmic = (TylerModelInterpretationContext) mic;
145            tmic.configureMethodSpecBuilder.endControlFlow();
146            mic.popModel();
147        }
148
149    }
150}