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 */ 027package ch.qos.logback.tyler.base; 028 029import ch.qos.logback.classic.joran.JoranConfigurator; 030import ch.qos.logback.classic.model.ConfigurationModel; 031import ch.qos.logback.classic.model.ContextNameModel; 032import ch.qos.logback.classic.model.LevelModel; 033import ch.qos.logback.classic.model.LoggerContextListenerModel; 034import ch.qos.logback.classic.model.LoggerModel; 035import ch.qos.logback.classic.model.RootLoggerModel; 036import ch.qos.logback.classic.model.PropertiesConfiguratorModel; 037import ch.qos.logback.classic.model.processor.LogbackClassicDefaultNestedComponentRules; 038import ch.qos.logback.core.Context; 039import ch.qos.logback.core.joran.event.SaxEventRecorder; 040import ch.qos.logback.core.joran.spi.JoranException; 041import ch.qos.logback.core.model.*; 042import ch.qos.logback.core.model.conditional.ByPropertiesConditionModel; 043import ch.qos.logback.core.model.conditional.ElseModel; 044import ch.qos.logback.core.model.conditional.IfModel; 045import ch.qos.logback.core.model.conditional.ThenModel; 046import ch.qos.logback.core.model.processor.DefaultProcessor; 047import ch.qos.logback.core.model.processor.ImportModelHandler; 048import ch.qos.logback.core.util.StatusPrinter2; 049import ch.qos.logback.tyler.base.handler.*; 050import ch.qos.logback.tyler.base.util.StringPrintStream; 051import com.squareup.javapoet.JavaFile; 052import com.squareup.javapoet.MethodSpec; 053import com.squareup.javapoet.TypeSpec; 054import org.xml.sax.InputSource; 055 056import java.io.ByteArrayInputStream; 057import java.io.IOException; 058import java.io.InputStream; 059import java.util.ArrayList; 060import java.util.Arrays; 061import java.util.List; 062 063public class ModelToJava { 064 065 066 final Context context; 067 068 public ModelToJava(Context context) { 069 this.context = context; 070 } 071 072 073 public Model extractModel(String input) throws JoranException { 074 InputStream inputStream = new ByteArrayInputStream(input.getBytes()); 075 InputSource inputSource = new InputSource(inputStream); 076 inputSource.setSystemId("UNKNOWN"); 077 078 JoranConfigurator joranConfigurator = new JoranConfigurator(); 079 joranConfigurator.setContext(context); 080 081 SaxEventRecorder recorder = joranConfigurator.populateSaxEventRecorder(inputSource); 082 Model top = joranConfigurator.buildModelFromSaxEventList(recorder.getSaxEventList()); 083 return top; 084 } 085 086 public StringBuffer toJavaAsStringBuffer(Model topModel) throws IOException { 087 TylerModelInterpretationContext tmic = new TylerModelInterpretationContext(context); 088 tmic.setTopModel(topModel); 089 090 LogbackClassicDefaultNestedComponentRules.addDefaultNestedComponentRegistryRules(tmic.getDefaultNestedComponentRegistry()); 091 092 DefaultProcessor defaultProcessor = new DefaultProcessor(context, tmic); 093 // this is where we link model classes to their Tyler-specific handlers 094 addModelHandlerAssociations(defaultProcessor); 095 096 defaultProcessor.process(topModel); 097 098 099 tmic.configureMethodSpecBuilder.addStatement("return ExecutionStatus.DO_NOT_INVOKE_NEXT_IF_ANY"); 100 MethodSpec configureMethodSpec = tmic.configureMethodSpecBuilder.build(); 101 102 tmic.fieldSpecs.forEach(tmic.tylerConfiguratorTSB.fieldSpecs::addFirst); 103 tmic.tylerConfiguratorTSB.methodSpecs.addFirst(configureMethodSpec); 104 105 for(String methodName: tmic.mapOfMethodSpecBuilders.keySet()) { 106 MethodSpec.Builder methodSpecBuilder = tmic.mapOfMethodSpecBuilders.get(methodName); 107 MethodSpec methodSpec = methodSpecBuilder.build(); 108 tmic.tylerConfiguratorTSB.methodSpecs.add(methodSpec); 109 } 110 111 TypeSpec tylerConfiguratorTypeSpec = tmic.tylerConfiguratorTSB.build(); 112 113 JavaFile.Builder javaFileBuilder = JavaFile.builder("com.example", tylerConfiguratorTypeSpec); 114 javaFileBuilder.skipJavaLangImports(true); 115 116 tmic.staticImportsList.forEach(sid -> javaFileBuilder.addStaticImport(sid.aClass(), sid.methodName())); 117 118 JavaFile javaFile = javaFileBuilder.indent(" ").build(); 119 StringBuffer sb = new StringBuffer(); 120 String s = javaFile.toString(); 121 javaFile.writeTo(sb); 122 return sb; 123 } 124 125 public String toJava(Model topModel) throws IOException { 126 StringBuffer buf = toJavaAsStringBuffer(topModel); 127 return buf.toString(); 128 } 129 130 public List<String> statusToStringList() { 131 List<String> resultList = new ArrayList<>(); 132 StatusPrinter2 statusPrinter2 = new StatusPrinter2(); 133 StringPrintStream sps = new StringPrintStream(System.out, false); 134 statusPrinter2.setPrintStream(sps); 135 statusPrinter2.print(context); 136 for(String s: sps.stringList) { 137 String[] split = s.split("\n"); 138 Arrays.stream(split).forEach(n -> resultList.add("// "+n)); 139 } 140 return resultList; 141 } 142 143 /** 144 * Associate model classes with their respective handlers. This is where ModelHandlers specific to 145 * Tyler are linked to the model processing. 146 * 147 * @param defaultProcessor 148 */ 149 private void addModelHandlerAssociations(DefaultProcessor defaultProcessor) { 150 defaultProcessor.addHandler(ConfigurationModel.class, ConfigurationModelHandler::makeInstance); 151 defaultProcessor.addHandler(PropertyModel.class, VariableModelHandler::makeInstance); 152 153 defaultProcessor.addHandler(ContextNameModel.class, ContextNameModelHandler::makeInstance); 154 defaultProcessor.addHandler(ImportModel.class, ImportModelHandler::makeInstance); 155 defaultProcessor.addHandler(DefineModel.class, DefineModelHandler::makeInstance); 156 defaultProcessor.addHandler(InsertFromJNDIModel.class, TylerInsertFromJNDIModelHandler::makeInstance); 157 158 defaultProcessor.addHandler(StatusListenerModel.class, StatusListenerModelHandler::makeInstance); 159 defaultProcessor.addHandler(ShutdownHookModel.class, ShutdownHookModelHandler::makeInstance); 160 defaultProcessor.addHandler(TimestampModel.class, TimestampModelHandler::makeInstance); 161 defaultProcessor.addHandler(PropertiesConfiguratorModel.class, TylerPropertiesConfiguratorModelHandler::makeInstance); 162 defaultProcessor.addHandler(AppenderModel.class, AppenderModelHandler::makeInstance); 163 defaultProcessor.addHandler(ImplicitModel.class, ImplicitModelHandler::makeInstance); 164 defaultProcessor.addHandler(LoggerModel.class, LoggerModelHandler::makeInstance); 165 defaultProcessor.addHandler(RootLoggerModel.class, RootLoggerModelHandler::makeInstance); 166 defaultProcessor.addHandler(LevelModel.class, LevelModelHandler::makeInstance); 167 defaultProcessor.addHandler(AppenderRefModel.class, AppenderRefModelHandler::makeInstance); 168 169 defaultProcessor.addHandler(IncludeModel.class, TylerIncludeModelHandler::makeInstance); 170 171 defaultProcessor.addHandler(LoggerContextListenerModel.class, LoggerContextListenerModelHandler::makeInstance); 172 defaultProcessor.addHandler(SequenceNumberGeneratorModel.class, SequenceNumberGeneratorModelHandler::makeInstance); 173 174 175 defaultProcessor.addHandler(ByPropertiesConditionModel.class, ByPropertiesConditionModelHandler::makeInstance); 176 defaultProcessor.addHandler(IfModel.class, IfModelHandler::makeInstance); 177 defaultProcessor.addHandler(ThenModel.class, ThenModelHandler::makeInstance); 178 defaultProcessor.addHandler(ElseModel.class, ElseModelHandler::makeInstance); 179 180 } 181 182}