001/** 002 * Logback: the reliable, generic, fast and flexible logging framework. 003 * Copyright (C) 1999-2022, QOS.ch. All rights reserved. 004 * 005 * This program and the accompanying materials are dual-licensed under 006 * either the terms of the Eclipse Public License v1.0 as published by 007 * the Eclipse Foundation 008 * 009 * or (per the licensee's choosing) 010 * 011 * under the terms of the GNU Lesser General Public License version 2.1 012 * as published by the Free Software Foundation. 013 */ 014package ch.qos.logback.core.model.processor; 015 016import java.util.ArrayList; 017import java.util.Collections; 018import java.util.HashMap; 019import java.util.List; 020import java.util.Map; 021import java.util.Properties; 022import java.util.Stack; 023import java.util.function.Supplier; 024 025import ch.qos.logback.core.Appender; 026import ch.qos.logback.core.Context; 027import ch.qos.logback.core.joran.GenericXMLConfigurator; 028import ch.qos.logback.core.joran.JoranConfiguratorBase; 029import ch.qos.logback.core.joran.JoranConstants; 030import ch.qos.logback.core.joran.spi.DefaultNestedComponentRegistry; 031import ch.qos.logback.core.joran.util.beans.BeanDescriptionCache; 032import ch.qos.logback.core.model.Model; 033import ch.qos.logback.core.model.util.VariableSubstitutionsHelper; 034import ch.qos.logback.core.spi.AppenderAttachable; 035import ch.qos.logback.core.spi.ContextAwareBase; 036import ch.qos.logback.core.spi.ContextAwarePropertyContainer; 037import ch.qos.logback.core.spi.PropertyContainer; 038 039public class ModelInterpretationContext extends ContextAwareBase implements ContextAwarePropertyContainer { 040 041 Stack<Object> objectStack; 042 Stack<Model> modelStack; 043 044 /** 045 * A supplier of JoranConfigurator instances. 046 * 047 * May be null. 048 * 049 * @since 1.5.5 050 */ 051 Supplier<? extends GenericXMLConfigurator> configuratorSupplier; 052 053 054 Map<String, Object> objectMap; 055 protected VariableSubstitutionsHelper variableSubstitutionsHelper; 056 protected Map<String, String> importMap; 057 058 final private BeanDescriptionCache beanDescriptionCache; 059 final DefaultNestedComponentRegistry defaultNestedComponentRegistry = new DefaultNestedComponentRegistry(); 060 List<DependencyDefinition> dependencyDefinitionList = new ArrayList<>(); 061 final List<String> startedDependees = new ArrayList<>(); 062 063 Object configuratorHint; 064 065 Model topModel; 066 067 public ModelInterpretationContext(Context context) { 068 this(context, null); 069 } 070 071 public ModelInterpretationContext(Context context, Object configuratorHint) { 072 this.context = context; 073 this.configuratorHint = configuratorHint; 074 this.objectStack = new Stack<>(); 075 this.modelStack = new Stack<>(); 076 this.beanDescriptionCache = new BeanDescriptionCache(context); 077 objectMap = new HashMap<>(5); 078 variableSubstitutionsHelper = new VariableSubstitutionsHelper(context); 079 importMap = new HashMap<>(5); 080 } 081 082 public ModelInterpretationContext(ModelInterpretationContext otherMic) { 083 this(otherMic.context, otherMic.configuratorHint); 084 importMap = new HashMap<>(otherMic.importMap); 085 variableSubstitutionsHelper = new VariableSubstitutionsHelper(context, otherMic.getCopyOfPropertyMap()); 086 defaultNestedComponentRegistry.duplicate(otherMic.getDefaultNestedComponentRegistry()); 087 createAppenderBags(); 088 } 089 090 public Map<String, Object> getObjectMap() { 091 return objectMap; 092 } 093 094 public void createAppenderBags() { 095 objectMap.put(JoranConstants.APPENDER_BAG, new HashMap<String, Appender<?>>()); 096 objectMap.put(JoranConstants.APPENDER_REF_BAG, new HashMap<String, AppenderAttachable<?>>()); 097 } 098 099 public Model getTopModel() { 100 return topModel; 101 } 102 103 public void setTopModel(Model topModel) { 104 this.topModel = topModel; 105 } 106 107 // modelStack ================================= 108 109 public void pushModel(Model m) { 110 modelStack.push(m); 111 } 112 113 public Model peekModel() { 114 return modelStack.peek(); 115 } 116 117 public boolean isModelStackEmpty() { 118 return modelStack.isEmpty(); 119 } 120 121 public Model popModel() { 122 return modelStack.pop(); 123 } 124 125 // =================== object stack 126 127 public Stack<Object> getObjectStack() { 128 return objectStack; 129 } 130 131 public boolean isObjectStackEmpty() { 132 return objectStack.isEmpty(); 133 } 134 135 public Object peekObject() { 136 return objectStack.peek(); 137 } 138 139 public void pushObject(Object o) { 140 objectStack.push(o); 141 } 142 143 public Object popObject() { 144 return objectStack.pop(); 145 } 146 147 public Object getObject(int i) { 148 return objectStack.get(i); 149 } 150 151 // ===================== END object stack 152 153 public Object getConfiguratorHint() { 154 return configuratorHint; 155 } 156 157 public void setConfiguratorHint(Object configuratorHint) { 158 this.configuratorHint = configuratorHint; 159 } 160 161 public BeanDescriptionCache getBeanDescriptionCache() { 162 return beanDescriptionCache; 163 } 164 165 public String subst(String ref) { 166 return variableSubstitutionsHelper.subst(ref); 167 } 168 169 170 public DefaultNestedComponentRegistry getDefaultNestedComponentRegistry() { 171 return defaultNestedComponentRegistry; 172 } 173 174 // ================================== dependencies 175 176 public void addDependencyDefinition(DependencyDefinition dd) { 177 dependencyDefinitionList.add(dd); 178 } 179 180 public List<DependencyDefinition> getDependencyDefinitions() { 181 return Collections.unmodifiableList(dependencyDefinitionList); 182 } 183 184 public List<String> getDependeeNamesForModel(Model model) { 185 List<String> dependencyList = new ArrayList<>(); 186 for (DependencyDefinition dd : dependencyDefinitionList) { 187 if (dd.getDepender() == model) { 188 dependencyList.add(dd.getDependee()); 189 } 190 } 191 return dependencyList; 192 } 193 194 public boolean hasDependers(String dependeeName) { 195 196 if (dependeeName == null || dependeeName.trim().length() == 0) { 197 new IllegalArgumentException("Empty dependeeName name not allowed here"); 198 } 199 200 for (DependencyDefinition dd : dependencyDefinitionList) { 201 if (dd.dependee.equals(dependeeName)) 202 return true; 203 } 204 205 return false; 206 } 207 208 209 public void markStartOfNamedDependee(String name) { 210 startedDependees.add(name); 211 } 212 213 public boolean isNamedDependeeStarted(String name) { 214 return startedDependees.contains(name); 215 } 216 217 // ========================================== object map 218 219 /** 220 * Add a property to the properties of this execution context. If the property 221 * exists already, it is overwritten. 222 */ 223 @Override 224 public void addSubstitutionProperty(String key, String value) { 225 variableSubstitutionsHelper.addSubstitutionProperty(key, value); 226 } 227 228 /** 229 * If a key is found in propertiesMap then return it. Otherwise, delegate to the 230 * context. 231 */ 232 public String getProperty(String key) { 233 return variableSubstitutionsHelper.getProperty(key); 234 } 235 236 @Override 237 public Map<String, String> getCopyOfPropertyMap() { 238 return variableSubstitutionsHelper.getCopyOfPropertyMap(); 239 } 240 241 // imports =================================================================== 242 243 /** 244 * Add an import to the importMao 245 * 246 * @param stem the class to import 247 * @param fqcn the fully qualified name of the class 248 * 249 * @since 1.3 250 */ 251 public void addImport(String stem, String fqcn) { 252 importMap.put(stem, fqcn); 253 } 254 255 public Map<String, String> getImportMapCopy() { 256 return new HashMap<>(importMap); 257 } 258 259 260 /** 261 * Given a stem, get the fully qualified name of the class corresponding to the 262 * stem. For unknown stems, returns the stem as is. If stem is null, null is 263 * returned. 264 * 265 * @param stem may be null 266 * @return fully qualified name of the class corresponding to the stem. For 267 * unknown stems, returns the stem as is. If stem is null, null is 268 * returned. 269 * @since 1.3 270 */ 271 public String getImport(String stem) { 272 if (stem == null) 273 return null; 274 275 String result = importMap.get(stem); 276 if (result == null) 277 return stem; 278 else 279 return result; 280 } 281 282 /** 283 * Returns a supplier of {@link GenericXMLConfigurator} instance. The returned value may be null. 284 * 285 * @return a supplier of {@link GenericXMLConfigurator} instance, may be null 286 */ 287 public Supplier<? extends GenericXMLConfigurator> getConfiguratorSupplier() { 288 return this.configuratorSupplier; 289 } 290 291 /** 292 * 293 * @param configuratorSupplier 294 */ 295 public void setConfiguratorSupplier(Supplier<? extends GenericXMLConfigurator> configuratorSupplier) { 296 this.configuratorSupplier = configuratorSupplier; 297 } 298}