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.ComponentModel; 032import ch.qos.logback.core.model.Model; 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.spi.LifeCycle; 037import ch.qos.logback.core.util.OptionHelper; 038import ch.qos.logback.tyler.base.TylerModelInterpretationContext; 039import ch.qos.logback.tyler.base.util.ClassUtil; 040import ch.qos.logback.tyler.base.util.VariableNameUtil; 041import com.squareup.javapoet.ClassName; 042import com.squareup.javapoet.MethodSpec; 043 044import static ch.qos.logback.tyler.base.TylerConstants.SETUP; 045 046public abstract class ComponentModelHandler extends ModelHandlerBase { 047 048 private boolean inError; 049 ImplicitModelHandlerData implicitModelHandlerData; 050 051 052 protected int instanceNum; 053 054 public ComponentModelHandler(Context context) { 055 super(context); 056 } 057 058 protected int getInstanceNumber() { 059 return instanceNum; 060 } 061 062 abstract String getTargetType(); 063 064 @Override 065 public void handle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { 066 067 ComponentModel componentModel = (ComponentModel) model; 068 TylerModelInterpretationContext tmic = (TylerModelInterpretationContext) mic; 069 070 String componentClassName = componentModel.getClassName(); 071 if (OptionHelper.isNullOrEmptyOrAllSpaces(componentClassName)) { 072 addWarn("Missing className. This should have been caught earlier."); 073 inError = true; 074 return; 075 } else { 076 componentClassName = mic.getImport(componentClassName); 077 } 078 079 addInfo("About to configure " + getTargetType() + " of type [" + componentClassName + "]"); 080 MethodSpec.Builder methodSpec = addJavaStatement(tmic, componentModel, componentClassName); 081 addAdditionalJavaStatement(methodSpec, componentModel); 082 this.implicitModelHandlerData = ImplicitModelHandlerData.makeInstance(this, methodSpec, componentClassName); 083 if(implicitModelHandlerData != null) { 084 mic.pushObject(implicitModelHandlerData); 085 } else { 086 addError("Could not make implicitModelHandlerData for ["+componentClassName+"]"); 087 model.markAsSkipped(); 088 inError = true; 089 } 090 } 091 092 protected void addAdditionalJavaStatement(MethodSpec.Builder methodSpec, ComponentModel componentModel) { 093 094 } 095 096 MethodSpec.Builder addJavaStatement(TylerModelInterpretationContext tmic, 097 ComponentModel componentModel, String componentClassName) { 098 099 100 String simpleName = ClassUtil.extractSimpleClassName(componentClassName); 101 102 103 ClassName desiredComponentCN = ClassName.get(ClassUtil.extractPackageName(componentClassName), 104 simpleName); 105 106 String variableName = VariableNameUtil.fullyQualifiedClassNameToVariableName(componentClassName); 107 108 String methodName = getMethodNameForComponent(simpleName); 109 MethodSpec.Builder methodSpecBuilder = MethodSpec.methodBuilder( 110 methodName).returns(void.class) 111 .addStatement("$1T $2N = new $1T()", desiredComponentCN, variableName) 112 .addStatement("$N.setContext($N)", variableName, tmic.getContextFieldSpec()); 113 114 return methodSpecBuilder; 115 } 116 117 protected String getMethodNameForComponent(String simpleName) { 118 // append instance number to method name to avoid name clashes 119 return SETUP + simpleName + "_" + instanceNum; 120 } 121 122 @Override 123 public void postHandle(ModelInterpretationContext mic, Model model) { 124 if (inError) { 125 return; 126 } 127 128 Object o = mic.peekObject(); 129 if (o != implicitModelHandlerData) { 130 addWarn("The object at the of the stack is not the implicitModelHandlerData pushed earlier."); 131 } else { 132 TylerModelInterpretationContext tmic = (TylerModelInterpretationContext) mic; 133 tmic.popObject(); 134 135 MethodSpec.Builder componentMethodBuilder = implicitModelHandlerData.methodSpecBuilder; 136 137 String variableName = implicitModelHandlerData.getVariableName(); 138 139 componentMethodBuilder.addCode("\n"); 140 componentMethodBuilder.beginControlFlow("if($N instanceof $T)", variableName, LifeCycle.class); 141 componentMethodBuilder.addStatement("(($T)$N).start()", LifeCycle.class, variableName); 142 componentMethodBuilder.endControlFlow(); 143 MethodSpec componentMethodSpec = componentMethodBuilder.build(); 144 145 tmic.tylerConfiguratorTSB.addMethod(componentMethodSpec); 146 147 linkMethodSpecWithConfigureMethod(tmic, componentMethodSpec); 148 149 } 150 151 } 152 153 protected void linkMethodSpecWithConfigureMethod(TylerModelInterpretationContext tmic, MethodSpec componentMethodSpec) { 154 tmic.configureMethodSpecBuilder.addStatement("$N()", componentMethodSpec); 155 } 156}