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