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.Appender;
031import ch.qos.logback.core.Context;
032import ch.qos.logback.core.model.AppenderModel;
033import ch.qos.logback.core.model.Model;
034import ch.qos.logback.core.model.processor.ModelHandlerBase;
035import ch.qos.logback.core.model.processor.ModelHandlerException;
036import ch.qos.logback.core.model.processor.ModelInterpretationContext;
037import ch.qos.logback.core.util.OptionHelper;
038import ch.qos.logback.core.util.StringUtil;
039import ch.qos.logback.tyler.base.TylerModelInterpretationContext;
040import ch.qos.logback.tyler.base.util.ClassUtil;
041import ch.qos.logback.tyler.base.util.VariableNameUtil;
042import com.squareup.javapoet.ClassName;
043import com.squareup.javapoet.MethodSpec;
044
045import static ch.qos.logback.tyler.base.TylerConstants.SETUP_APPENDER;
046
047public class AppenderModelHandler extends ModelHandlerBase {
048
049    String appenderVariableName;
050
051    ImplicitModelHandlerData classAndMethodSpecBuilderTuple;
052
053    private boolean skipped = false;
054    boolean inError = false;
055
056    public AppenderModelHandler(Context context) {
057        super(context);
058    }
059
060    @SuppressWarnings("rawtypes")
061    static public ModelHandlerBase makeInstance(Context context, ModelInterpretationContext mic) {
062        return new AppenderModelHandler(context);
063    }
064
065    @Override
066    @SuppressWarnings("unchecked")
067    public void handle(ModelInterpretationContext mic, Model model) throws ModelHandlerException {
068
069
070        AppenderModel appenderModel = (AppenderModel) model;
071
072        TylerModelInterpretationContext tmic = (TylerModelInterpretationContext) mic;
073
074        String appenderName = tmic.subst(appenderModel.getName());
075
076        //        if (!mic.hasDependers(appenderName)) {
077        //            addWarn("Appender named [" + appenderName + "] not referenced. Skipping further processing.");
078        //            skipped = true;
079        //            appenderModel.markAsSkipped();
080        //            return;
081        //        }
082
083        String originalClassName = appenderModel.getClassName();
084        String className = mic.getImport(originalClassName);
085        this.appenderVariableName = VariableNameUtil.appenderNameToVariableName(appenderName);
086        MethodSpec.Builder methodSpec = addJavaStatementForAppenderInitialization(tmic, appenderName, className);
087        try {
088            Class appenderClass = Class.forName(className);
089            classAndMethodSpecBuilderTuple = new ImplicitModelHandlerData(appenderClass, appenderVariableName,
090                    methodSpec);
091            mic.pushObject(classAndMethodSpecBuilderTuple);
092        } catch (ClassNotFoundException e) {
093            addError("Could not find class", e);
094            inError = true;
095        }
096
097    }
098
099    MethodSpec.Builder addJavaStatementForAppenderInitialization(TylerModelInterpretationContext tmic,
100            String appenderName, String fullyQualifiedAppenderClassName) {
101
102        ClassName optionHelperCN = ClassName.get(OptionHelper.class);
103        ClassName appenderIntefaceCN = ClassName.get(Appender.class);
104        ClassName desiredAppenderCN = ClassName.get(ClassUtil.extractPackageName(fullyQualifiedAppenderClassName),
105                ClassUtil.extractSimpleClassName(fullyQualifiedAppenderClassName));
106
107        String fistLetterCapitalizedAppenderName = StringUtil.capitalizeFirstLetter(appenderName);
108
109        MethodSpec.Builder appenderSetupMethodSpec = MethodSpec.methodBuilder(SETUP_APPENDER + fistLetterCapitalizedAppenderName)
110                .returns(Appender.class).addStatement("$T " + this.appenderVariableName, desiredAppenderCN)
111                .beginControlFlow("try")
112                .addStatement(this.appenderVariableName + " = ($1T) $2T.instantiateByClassName($3S, $4T.class, $5N)",
113                        desiredAppenderCN, optionHelperCN, fullyQualifiedAppenderClassName, appenderIntefaceCN,
114                        tmic.getContextFieldSpec())
115                .nextControlFlow("catch ($T oops)", Exception.class)
116                .addStatement("addError(\"Could not create an Appender of type [\" + $S + \"].\", oops)",
117                        fullyQualifiedAppenderClassName).addStatement("return null")
118                .endControlFlow()
119                .addStatement(this.appenderVariableName + ".setContext($N)", tmic.getContextFieldSpec())
120                .addStatement(this.appenderVariableName + ".setName($S)", appenderName);
121
122        return appenderSetupMethodSpec;
123    }
124
125    @Override
126    public void postHandle(ModelInterpretationContext mic, Model model) {
127        if (inError) {
128            return;
129        }
130
131        Object o = mic.peekObject();
132        if (o != classAndMethodSpecBuilderTuple) {
133            addWarn("The object at the of the stack is not the ClassAndMethodSpecTuple pushed earlier.");
134        } else {
135            TylerModelInterpretationContext tmic = (TylerModelInterpretationContext) mic;
136            tmic.popObject();
137
138            MethodSpec.Builder appenderMethodBuilder = classAndMethodSpecBuilderTuple.methodSpecBuilder;
139
140            // start the appender
141            appenderMethodBuilder.addCode("\n");
142            appenderMethodBuilder.addStatement("$N.start()", appenderVariableName);
143            appenderMethodBuilder.addStatement("return $N", appenderVariableName);
144            MethodSpec appenderMethodSpec = appenderMethodBuilder.build();
145
146            tmic.tylerConfiguratorTSB.addMethod(appenderMethodSpec);
147
148            tmic.configureMethodSpecBuilder.addStatement("$T $N = $N()", Appender.class, appenderVariableName, appenderMethodSpec);
149        }
150
151    }
152}