001/*
002 *  Copyright (c) 2004-2024 QOS.ch
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
025package ch.qos.logback.tyler.base.handler;
026
027import ch.qos.logback.classic.model.PropertiesConfiguratorModel;
028import ch.qos.logback.classic.model.processor.PropertiesConfiguratorModelHandler;
029import ch.qos.logback.core.Context;
030import ch.qos.logback.core.model.IncludeModel;
031import ch.qos.logback.core.model.Model;
032import ch.qos.logback.core.model.processor.IncludeModelHandler;
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;
038import com.squareup.javapoet.MethodSpec;
039import com.squareup.javapoet.ParameterSpec;
040
041import javax.lang.model.element.Modifier;
042
043public class TylerIncludeModelHandler extends ModelHandlerBase {
044
045    final static String INCLUDE_METHOD_NAME = "include";
046
047    public TylerIncludeModelHandler(Context context) {
048        super(context);
049    }
050
051    static public ModelHandlerBase makeInstance(Context context, ModelInterpretationContext ic) {
052        return new TylerIncludeModelHandler(context);
053    }
054
055    @Override
056    protected Class<IncludeModel> getSupportedModelClass() {
057        return IncludeModel.class;
058    }
059
060    @Override
061    public void handle(ModelInterpretationContext mic, Model model) throws ModelHandlerException {
062        IncludeModel includeModel = (IncludeModel) model;
063        TylerModelInterpretationContext tmic = (TylerModelInterpretationContext) mic;
064        addJavaStatement(tmic, includeModel);
065    }
066
067    protected void addJavaStatement(TylerModelInterpretationContext tmic, IncludeModel originalModel) {
068
069        addIncludeMethod(tmic);
070        tmic.configureMethodSpecBuilder.addStatement("$N($S, $S, $S, $S)", INCLUDE_METHOD_NAME, originalModel.getFile(), originalModel.getUrl(),
071                        originalModel.getResource(), originalModel.getOptional());
072
073    }
074
075    void addIncludeMethod(TylerModelInterpretationContext tmic) {
076
077
078        // insert method at most once
079        if(tmic.mapOfMethodSpecBuilders.containsKey(INCLUDE_METHOD_NAME))
080            return;
081
082        // code to produce
083        //
084        // void include(String fileStr, String urlStr, String resourceStr, String optionalStr) {
085
086        //    IncludeModel includeModel = new IncludeModel();
087        //
088        //    includeModel.setFile(fileStr);  // use actual string value of pcModel.getFile()
089        //
090        //    includeModel.setUrl(urlStr); // use actual string value of pcModel.getUrl()
091        //    includeModel.setResource(resourceStr);
092        //    includeModel.setOptional(optionalStr);
093        //    IncludeModelHandler includeModelHandler = new IncludeModelHandler(context);
094        //    try {
095        //      Model modelFromIncludedFile = includeModelHandler.buildModelFromIncludedFile(this, includeModel);
096        //      processModelFromIncludedFile(modelFromIncludedFile);
097        //    } catch(ModelHandlerException e) {
098        //       addError("Failed to process IncludeModelHandler", e);
099        //    }
100        // }
101
102        String includeModelVarName = "includeModel";
103        String imhVarName = "includeModelHandler";
104        String mfifVarName = "modelFromIncludedFile";
105
106        final String fileStrVarName = "fileStr";
107        final String urlStrVarName = "urlStr";
108        final String resourceStrVarName = "resourceStr";
109        final String optionalStrVarName = "optionalStr";
110
111
112        final ParameterSpec fileStr_ParameterSpec = ParameterSpec.builder(String.class, fileStrVarName).build();
113        final ParameterSpec urStr_ParameterSpec = ParameterSpec.builder(String.class, urlStrVarName).build();
114        final ParameterSpec resourceStr_ParameterSpec = ParameterSpec.builder(String.class, resourceStrVarName).build();
115        final ParameterSpec optionalStr_ParameterSpec = ParameterSpec.builder(String.class, optionalStrVarName).build();
116
117        MethodSpec.Builder msBuilder = MethodSpec.methodBuilder(INCLUDE_METHOD_NAME).addModifiers(Modifier.PRIVATE).addParameter(fileStr_ParameterSpec)
118                        .addParameter(urStr_ParameterSpec).addParameter(resourceStr_ParameterSpec).addParameter(optionalStr_ParameterSpec).returns(void.class);
119
120        msBuilder.addStatement("$1T $2N = new $1T()", IncludeModel.class, includeModelVarName);
121        msBuilder.addStatement("$N.setFile(subst($N))", includeModelVarName, fileStrVarName);
122        msBuilder.addStatement("$N.setUrl(subst($N))", includeModelVarName, urlStrVarName);
123        msBuilder.addStatement("$N.setResource(subst($N))", includeModelVarName, resourceStrVarName);
124        msBuilder.addStatement("$N.setOptional(subst($N))", includeModelVarName, optionalStrVarName);
125        msBuilder.addStatement("$1T $2N = new $1T($3N)", IncludeModelHandler.class, imhVarName, tmic.getContextFieldSpec());
126
127        // "this is the calling TylerConfigurator instance of type ContextAwarePropertyContainer"
128        msBuilder.beginControlFlow("try");
129        msBuilder.addStatement("$T $N = $N.buildModelFromIncludedFile(this, $N)", Model.class, mfifVarName, imhVarName, includeModelVarName);
130        msBuilder.addStatement("processModelFromIncludedFile($N)", mfifVarName);
131        msBuilder.nextControlFlow("catch($T e)", ModelHandlerException.class);
132        msBuilder.addStatement("addError(\"Failed to process IncludeModelHandler\", e)");
133        msBuilder.endControlFlow();
134
135        tmic.mapOfMethodSpecBuilders.put(INCLUDE_METHOD_NAME, msBuilder);
136
137        msBuilder.addJavadoc("""
138        <p>Warning: please note that at translation time logback-tyler usually does not have  
139        access to the included file.<p>
140        
141        <p>It follows that the code in this method (produced by logback-tyler) falls back to invoking 
142        logback-classic's default configurator, i.e. JoranConfigurator which will invoke an XML 
143        parser.</p>
144        
145        <p>If you wish to avoid calling <code>JoranConfigurator</code>, then you can insert 
146        the contents of the included XML file into the encompassing file manually before performing 
147        the translation.</p>
148        
149        <p>Also note that PropertiesConfigurator introduced in version 1.5.8, allows for setting 
150        logger levels via a properties file. The location of properties files can be specified 
151        as a file path as well a URL via HTTP or HTTPS protocols. Watching files and 
152        reconfiguration upon change are also supported. Logback-tyler supports 
153        <code>PropertiesConfigurator</code>.</p>
154                
155        """);
156    }
157}