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}