View Javadoc

1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2009, 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.joran.action;
15  
16  import java.util.Stack;
17  
18  import org.xml.sax.Attributes;
19  
20  import ch.qos.logback.core.joran.spi.InterpretationContext;
21  import ch.qos.logback.core.joran.spi.NoAutoStartUtil;
22  import ch.qos.logback.core.joran.spi.Pattern;
23  import ch.qos.logback.core.joran.util.PropertySetter;
24  import ch.qos.logback.core.spi.ContextAware;
25  import ch.qos.logback.core.spi.LifeCycle;
26  import ch.qos.logback.core.util.AggregationType;
27  import ch.qos.logback.core.util.Loader;
28  import ch.qos.logback.core.util.OptionHelper;
29  
30  /**
31   * This action is responsible for tying together a parent object with a child
32   * element for which there is no explicit rule.
33   * 
34   * @author Ceki Gülcü
35   */
36  public class NestedComplexPropertyIA extends ImplicitAction {
37  
38    // actionDataStack contains ActionData instances
39    // We use a stack of ActionData objects in order to support nested
40    // elements which are handled by the same NestedComplexPropertyIA instance.
41    // We push a ActionData instance in the isApplicable method (if the
42    // action is applicable) and pop it in the end() method.
43    // The XML well-formedness property will guarantee that a push will eventually
44    // be followed by a corresponding pop.
45    Stack<IADataForComplexProperty> actionDataStack = new Stack<IADataForComplexProperty>();
46  
47    public boolean isApplicable(Pattern pattern, Attributes attributes,
48        InterpretationContext ic) {
49  
50      String nestedElementTagName = pattern.peekLast();
51  
52      // calling ic.peekObject with an empty stack will throw an exception
53      if (ic.isEmpty()) {
54        return false;
55      }
56  
57      Object o = ic.peekObject();
58      PropertySetter parentBean = new PropertySetter(o);
59      parentBean.setContext(context);
60  
61      AggregationType aggregationType = parentBean
62          .computeAggregationType(nestedElementTagName);
63  
64      switch (aggregationType) {
65      case NOT_FOUND:
66      case AS_BASIC_PROPERTY:
67      case AS_BASIC_PROPERTY_COLLECTION:
68        return false;
69  
70        // we only push action data if NestComponentIA is applicable
71      case AS_COMPLEX_PROPERTY_COLLECTION:
72      case AS_COMPLEX_PROPERTY:
73        IADataForComplexProperty ad = new IADataForComplexProperty(parentBean,
74            aggregationType, nestedElementTagName);
75        actionDataStack.push(ad);
76  
77        return true;
78      default:
79        addError("PropertySetter.computeAggregationType returned "
80            + aggregationType);
81        return false;
82      }
83    }
84  
85    public void begin(InterpretationContext ec, String localName,
86        Attributes attributes) {
87      // LogLog.debug("in NestComponentIA begin method");
88      // get the action data object pushed in isApplicable() method call
89      IADataForComplexProperty actionData = (IADataForComplexProperty) actionDataStack
90          .peek();
91  
92      String className = attributes.getValue(CLASS_ATTRIBUTE);
93      // perform variable name substitution
94      className = ec.subst(className);
95  
96      Class componentClass = null;
97      try {
98  
99        if (!OptionHelper.isEmpty(className)) {
100         componentClass = Loader.loadClass(className, context);
101       } else {
102         // guess class name via implicit rules
103         PropertySetter parentBean = actionData.parentBean;
104         componentClass = parentBean.getClassNameViaImplicitRules(actionData
105             .getComplexPropertyName(), actionData.getAggregationType(), ec
106             .getDefaultNestedComponentRegistry());
107       }
108 
109       if (componentClass == null) {
110         actionData.inError = true;
111         String errMsg = "Could not find an appropriate class for property ["
112             + localName + "]";
113         addError(errMsg);
114         return;
115       }
116 
117       if (OptionHelper.isEmpty(className)) {
118         addInfo("Assuming default type [" + componentClass.getName()
119             + "] for [" + localName + "] property");
120       }
121 
122       actionData.setNestedComplexProperty(componentClass.newInstance());
123 
124       // pass along the repository
125       if (actionData.getNestedComplexProperty() instanceof ContextAware) {
126         ((ContextAware) actionData.getNestedComplexProperty())
127             .setContext(this.context);
128       }
129       // getLogger().debug(
130       addInfo("Pushing component [" + localName
131           + "] on top of the object stack.");
132       ec.pushObject(actionData.getNestedComplexProperty());
133 
134     } catch (Exception oops) {
135       actionData.inError = true;
136       String msg = "Could not create component [" + localName + "] of type ["
137           + className + "]";
138       addError(msg, oops);
139     }
140 
141   }
142 
143   public void end(InterpretationContext ec, String tagName) {
144 
145     // pop the action data object pushed in isApplicable() method call
146     // we assume that each this begin
147     IADataForComplexProperty actionData = (IADataForComplexProperty) actionDataStack
148         .pop();
149 
150     if (actionData.inError) {
151       return;
152     }
153 
154     PropertySetter nestedBean = new PropertySetter(actionData
155         .getNestedComplexProperty());
156     nestedBean.setContext(context);
157 
158     // have the nested element point to its parent if possible
159     if (nestedBean.computeAggregationType("parent") == AggregationType.AS_COMPLEX_PROPERTY) {
160       nestedBean.setComplexProperty("parent", actionData.parentBean.getObj());
161     }
162 
163     // start the nested complex property if it implements LifeCycle and is not
164     // marked with a @NoAutoStart annotation
165     Object nestedComplexProperty = actionData.getNestedComplexProperty();
166     if (nestedComplexProperty instanceof LifeCycle
167         && NoAutoStartUtil.notMarkedWithNoAutoStart(nestedComplexProperty)) {
168       ((LifeCycle) nestedComplexProperty).start();
169     }
170 
171     Object o = ec.peekObject();
172 
173     if (o != actionData.getNestedComplexProperty()) {
174       addError("The object on the top the of the stack is not the component pushed earlier.");
175     } else {
176       ec.popObject();
177       // Now let us attach the component
178       switch (actionData.aggregationType) {
179       case AS_COMPLEX_PROPERTY:
180         actionData.parentBean.setComplexProperty(tagName, actionData
181             .getNestedComplexProperty());
182 
183         break;
184       case AS_COMPLEX_PROPERTY_COLLECTION:
185         actionData.parentBean.addComplexProperty(tagName, actionData
186             .getNestedComplexProperty());
187 
188         break;
189       }
190     }
191   }
192 
193 }