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
028    package ch.qos.logback.tyler.base;
029
030    import ch.qos.logback.classic.Level;
031    import ch.qos.logback.classic.LoggerContext;
032    import ch.qos.logback.classic.spi.Configurator;
033    import ch.qos.logback.classic.tyler.TylerConfiguratorBase;
034    import ch.qos.logback.core.Context;
035    import ch.qos.logback.core.model.processor.ModelInterpretationContext;
036    import ch.qos.logback.tyler.base.spi.StaticImportData;
037    import ch.qos.logback.tyler.base.util.VariableNameUtil;
038    import com.squareup.javapoet.*;
039
040    import javax.lang.model.element.Modifier;
041
042    import java.util.*;
043
044    import static ch.qos.logback.tyler.base.TylerConstants.*;
045
046    public class TylerModelInterpretationContext extends ModelInterpretationContext {
047
048        final public TypeSpec.Builder tylerConfiguratorTSB;
049        final public MethodSpec.Builder configureMethodSpecBuilder;
050        final public Map<String, MethodSpec.Builder> mapOfMethodSpecBuilders = new LinkedHashMap<>();
051
052        final FieldSpec contextFieldSpec = FieldSpec.builder(LoggerContext.class, CONTEXT_FIELD_NAME, Modifier.PRIVATE).build();
053        final ParameterSpec contextParameterSpec = ParameterSpec.builder(LoggerContext.class, LOGGER_CONTEXT_PARAMETER_NAME).build();
054        final ParameterSpec levelParameterSpec = ParameterSpec.builder(Level.class, LEVEL_FIELD_NAME).build();
055
056        final List<FieldSpec> appenderFieldSpecs = new ArrayList<>();
057
058        final List<StaticImportData> staticImportsList = new ArrayList<>();
059
060        public TylerModelInterpretationContext(Context context) {
061            super(context);
062            this.configureMethodSpecBuilder = initializeConfigureMethodSpecBuilder();
063            this.tylerConfiguratorTSB = initializeTylerConfiguratorTSB();
064        }
065
066        public void addStaticImport(StaticImportData sid) {
067            if (!staticImportsList.contains(sid))
068                staticImportsList.add(sid);
069        }
070
071        TypeSpec.Builder initializeTylerConfiguratorTSB() {
072            //MethodSpec setupLoggerMS = makeSetupLoggerMethodSpec();
073
074            TypeSpec.Builder tsb = TypeSpec.classBuilder(TylerConstants.TYLER_CONFIGURATOR).addJavadoc("""
075                                            
076                                            <p>This file was generated by logback-tyler version %s</p>
077                                            
078                                            <p>Eventual errors and warnings are appended at the end.</p>                    
079                                            
080                                            <p>You may experiment with logback.xml to Java translation, i.e. 
081                                            TylerConfigurator generation, at the following URL:</p>
082                                            
083                                            <p>     https://logback.qos.ch/translator/services/xml2Java.html </p>
084                                            
085                                            <p>This generated TylerConfigurator class is intended to be copied and integrated 
086                                            into the user's project as a custom configurator. It will configure logback 
087                                            without XML. You are free to rename TylerConfigurator as you wish.</p>
088                                            
089                                            <p>It requires logback-classic version %s or later at runtime.</p>
090                                            
091                                            <p>Custom configurators are looked up via Java's service-provide facility. If a 
092                                            custom provider is found, it takes precedence over logback's own configurators, 
093                                            e.g. DefaultJoranConfigurator.</p>
094                                            
095                                            <p>To install your custom configurator to your project, add a 
096                                            provider-configuration file to the following path:</p> 
097                                            
098                                            <pre>  META-INF/services/ch.qos.logback.classic.spi.Configurator</pre>
099                                            
100                                            <p>This provider-configuration file should contain a line with the fully 
101                                            qualified class name of your tyler configurator.</p>
102                                            
103                                            <p>See also item 1 of 'Configuration at initialization' section at </p>
104                                            
105                                            <p>  https://logback.qos.ch/manual/configuration.html#auto_configuration</p> 
106                                            
107                                            <p>With recent versions of logback and logback-tyler you can still
108                                            configure logger levels dynamically using properties files. Note that
109                                            configuration files in properties format can be watched for
110                                            changes. See the documentation on PropertiesConfigurator for more details.</p>
111                                            
112                                            <p>https://logback.qos.ch/manual/configuration.html#propertiesConfigurator</p>
113                                            
114                                            <p>Keep in mind that by integrating a .properties configuration file info 
115                                            your tyler configurator, you can still change logger levels dynamically, without 
116                                            redeploying your application.</p>
117                                            
118                                            """.formatted(TYLER_VERSION, REQUIRED_LOGBACK_VERSION))
119                            .addSuperinterface(Configurator.class)
120                            .superclass(TylerConfiguratorBase.class)
121                            .addModifiers(Modifier.PUBLIC);
122            return tsb;
123        }
124
125        private MethodSpec.Builder initializeConfigureMethodSpecBuilder() {
126            MethodSpec.Builder msb = MethodSpec.methodBuilder(CONFIGURE_METHOD_NAME).addJavadoc("""
127                                            <p>This method performs configuration per {@link $T} interface.</p>
128                                            
129                                            <p>If <code>TylerConfigurator</code> is installed as a configurator service, this 
130                                            method will be called by logback-classic during initialization.</p>
131                                            """, Configurator.class).addAnnotation(Override.class).addModifiers(Modifier.PUBLIC).addParameter(contextParameterSpec)
132                            .returns(Configurator.ExecutionStatus.class)
133                            .addStatement("$N($N)", TylerConfiguratorBase.SET_CONTEXT_METHOD_NAME, contextParameterSpec);
134
135            return msb;
136        }
137
138        public FieldSpec getContextFieldSpec() {
139            return contextFieldSpec;
140        }
141
142//        Shows how to build fields with generic types
143//
144//        public FieldSpec createAppenderBagSpec() {
145//            ParameterizedTypeName mapTypeName = ParameterizedTypeName.get(Map.class, String.class, Appender.class);
146//            FieldSpec.Builder fieldSpecBuilder = FieldSpec.builder(mapTypeName, TYLER_APPENDER_BAG_FIELD_NAME).addModifiers(Modifier.PROTECTED, Modifier.FINAL);
147//            fieldSpecBuilder.initializer("new $T<>()", HashMap.class);
148//            fieldSpecBuilder.addJavadoc("A map used to reference appenders during configuration.");
149//
150//            return fieldSpecBuilder.build();
151//        }
152
153        public FieldSpec createAppenderFieldSpec(ClassName desiredAppenderCN, String appenderName) {
154            String appenderVariableName = VariableNameUtil.appenderNameToVariableName(appenderName);
155            FieldSpec.Builder fieldSpecBuilder = FieldSpec.builder(desiredAppenderCN, appenderVariableName).addModifiers(Modifier.PROTECTED);
156            fieldSpecBuilder.addJavadoc("Appender variable referencing the appender named $S.", appenderName);
157            return fieldSpecBuilder.build();
158        }
159
160        public Collection<FieldSpec> getAppenderFieldSpecs() {
161            return appenderFieldSpecs;
162        }
163    }