1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2022, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.core.model.processor;
15  
16  import java.util.ArrayList;
17  import java.util.Collections;
18  import java.util.HashMap;
19  import java.util.List;
20  import java.util.Map;
21  import java.util.Properties;
22  import java.util.Stack;
23  import java.util.function.Supplier;
24  
25  import ch.qos.logback.core.Appender;
26  import ch.qos.logback.core.Context;
27  import ch.qos.logback.core.joran.GenericXMLConfigurator;
28  import ch.qos.logback.core.joran.JoranConfiguratorBase;
29  import ch.qos.logback.core.joran.JoranConstants;
30  import ch.qos.logback.core.joran.spi.DefaultNestedComponentRegistry;
31  import ch.qos.logback.core.joran.util.beans.BeanDescriptionCache;
32  import ch.qos.logback.core.model.Model;
33  import ch.qos.logback.core.model.util.VariableSubstitutionsHelper;
34  import ch.qos.logback.core.spi.AppenderAttachable;
35  import ch.qos.logback.core.spi.ContextAwareBase;
36  import ch.qos.logback.core.spi.ContextAwarePropertyContainer;
37  import ch.qos.logback.core.spi.PropertyContainer;
38  
39  public class ModelInterpretationContext extends ContextAwareBase implements ContextAwarePropertyContainer {
40  
41      Stack<Object> objectStack;
42      Stack<Model> modelStack;
43  
44      /**
45       * A supplier of JoranConfigurator instances.
46       *
47       * May be null.
48       *
49       * @since 1.5.5
50       */
51      Supplier<? extends GenericXMLConfigurator> configuratorSupplier;
52  
53  
54      Map<String, Object> objectMap;
55      protected VariableSubstitutionsHelper variableSubstitutionsHelper;
56      protected Map<String, String> importMap;
57  
58      final private BeanDescriptionCache beanDescriptionCache;
59      final DefaultNestedComponentRegistry defaultNestedComponentRegistry = new DefaultNestedComponentRegistry();
60      List<DependencyDefinition> dependencyDefinitionList = new ArrayList<>();
61      final List<String> startedDependees = new ArrayList<>();
62  
63      Object configuratorHint;
64  
65      Model topModel;
66  
67      public ModelInterpretationContext(Context context) {
68          this(context, null);
69      }
70  
71      public ModelInterpretationContext(Context context, Object configuratorHint) {
72          this.context = context;
73          this.configuratorHint = configuratorHint;
74          this.objectStack = new Stack<>();
75          this.modelStack = new Stack<>();
76          this.beanDescriptionCache = new BeanDescriptionCache(context);
77          objectMap = new HashMap<>(5);
78          variableSubstitutionsHelper = new VariableSubstitutionsHelper(context);
79          importMap = new HashMap<>(5);
80      }
81  
82      public ModelInterpretationContext(ModelInterpretationContext otherMic) {
83          this(otherMic.context, otherMic.configuratorHint);
84          importMap = new HashMap<>(otherMic.importMap);
85          variableSubstitutionsHelper =  new VariableSubstitutionsHelper(context, otherMic.getCopyOfPropertyMap());
86          defaultNestedComponentRegistry.duplicate(otherMic.getDefaultNestedComponentRegistry());
87          createAppenderBags();
88      } 
89          
90      public Map<String, Object> getObjectMap() {
91          return objectMap;
92      }
93  
94      public void createAppenderBags() {
95          objectMap.put(JoranConstants.APPENDER_BAG, new HashMap<String, Appender<?>>());
96          objectMap.put(JoranConstants.APPENDER_REF_BAG, new HashMap<String, AppenderAttachable<?>>());
97      }
98  
99      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 
167         String substituted = variableSubstitutionsHelper.subst(ref);
168         if(ref != null && !ref.equals(substituted)) {
169             addInfo("value \""+substituted+"\" substituted for \""+ref+"\"");
170         }
171         return substituted;
172     }
173 
174 
175     public DefaultNestedComponentRegistry getDefaultNestedComponentRegistry() {
176         return defaultNestedComponentRegistry;
177     }
178 
179     // ================================== dependencies
180 
181     public void addDependencyDefinition(DependencyDefinition dd) {
182         dependencyDefinitionList.add(dd);
183     }
184 
185     public List<DependencyDefinition> getDependencyDefinitions() {
186         return Collections.unmodifiableList(dependencyDefinitionList);
187     }
188 
189     public List<String> getDependeeNamesForModel(Model model) {
190         List<String> dependencyList = new ArrayList<>();
191         for (DependencyDefinition dd : dependencyDefinitionList) {
192             if (dd.getDepender() == model) {
193                dependencyList.add(dd.getDependee());
194             }
195         }
196         return dependencyList;
197     }
198 
199     public boolean hasDependers(String dependeeName) {
200 
201         if (dependeeName == null || dependeeName.trim().length() == 0) {
202             new IllegalArgumentException("Empty dependeeName name not allowed here");
203         }
204 
205         for (DependencyDefinition dd : dependencyDefinitionList) {
206             if (dd.dependee.equals(dependeeName))
207                 return true;
208         }
209 
210         return false;
211     }
212 
213 
214     public void markStartOfNamedDependee(String name) {
215         startedDependees.add(name);
216     }
217 
218     public boolean isNamedDependeeStarted(String name) {
219         return startedDependees.contains(name);
220     }
221 
222     // ========================================== object map
223 
224     /**
225      * Add a property to the properties of this execution context. If the property
226      * exists already, it is overwritten.
227      */
228     @Override
229     public void addSubstitutionProperty(String key, String value) {
230         variableSubstitutionsHelper.addSubstitutionProperty(key, value);
231     }
232 
233     /**
234      * If a key is found in propertiesMap then return it. Otherwise, delegate to the
235      * context.
236      */
237     public String getProperty(String key) {
238       return  variableSubstitutionsHelper.getProperty(key);
239     }
240 
241     @Override
242     public Map<String, String> getCopyOfPropertyMap() {
243         return variableSubstitutionsHelper.getCopyOfPropertyMap();
244     }
245 
246     // imports ===================================================================
247 
248     /**
249      * Add an import to the importMao
250      * 
251      * @param stem the class to import
252      * @param fqcn the fully qualified name of the class
253      * 
254      * @since 1.3
255      */
256     public void addImport(String stem, String fqcn) {
257         importMap.put(stem, fqcn);
258     }
259 
260     public Map<String, String> getImportMapCopy() {
261         return new HashMap<>(importMap);
262     }
263 
264     
265     /**
266      * Given a stem, get the fully qualified name of the class corresponding to the
267      * stem. For unknown stems, returns the stem as is. If stem is null, null is
268      * returned.
269      * 
270      * @param stem may be null
271      * @return fully qualified name of the class corresponding to the stem. For
272      *         unknown stems, returns the stem as is. If stem is null, null is
273      *         returned.
274      * @since 1.3
275      */
276     public String getImport(String stem) {
277         if (stem == null)
278             return null;
279 
280         String result = importMap.get(stem);
281         if (result == null)
282             return stem;
283         else
284             return result;
285     }
286 
287     /**
288      * Returns a supplier of {@link GenericXMLConfigurator} instance. The returned value may be null.
289      *
290      * @return a supplier of {@link GenericXMLConfigurator} instance, may be null
291      */
292     @Override
293     public Supplier<? extends GenericXMLConfigurator> getConfiguratorSupplier() {
294         return this.configuratorSupplier;
295     }
296 
297     /**
298      *
299      * @param configuratorSupplier
300      */
301     public void setConfiguratorSupplier(Supplier<? extends GenericXMLConfigurator> configuratorSupplier) {
302         this.configuratorSupplier = configuratorSupplier;
303     }
304 }