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