View Javadoc

1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2011, 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       //addInfo("Pushing component [" + localName
130       //    + "] on top of the object stack.");
131       ec.pushObject(actionData.getNestedComplexProperty());
132 
133     } catch (Exception oops) {
134       actionData.inError = true;
135       String msg = "Could not create component [" + localName + "] of type ["
136           + className + "]";
137       addError(msg, oops);
138     }
139 
140   }
141 
142   public void end(InterpretationContext ec, String tagName) {
143 
144     // pop the action data object pushed in isApplicable() method call
145     // we assume that each this begin
146     IADataForComplexProperty actionData = (IADataForComplexProperty) actionDataStack
147         .pop();
148 
149     if (actionData.inError) {
150       return;
151     }
152 
153     PropertySetter nestedBean = new PropertySetter(actionData
154         .getNestedComplexProperty());
155     nestedBean.setContext(context);
156 
157     // have the nested element point to its parent if possible
158     if (nestedBean.computeAggregationType("parent") == AggregationType.AS_COMPLEX_PROPERTY) {
159       nestedBean.setComplexProperty("parent", actionData.parentBean.getObj());
160     }
161 
162     // start the nested complex property if it implements LifeCycle and is not
163     // marked with a @NoAutoStart annotation
164     Object nestedComplexProperty = actionData.getNestedComplexProperty();
165     if (nestedComplexProperty instanceof LifeCycle
166         && NoAutoStartUtil.notMarkedWithNoAutoStart(nestedComplexProperty)) {
167       ((LifeCycle) nestedComplexProperty).start();
168     }
169 
170     Object o = ec.peekObject();
171 
172     if (o != actionData.getNestedComplexProperty()) {
173       addError("The object on the top the of the stack is not the component pushed earlier.");
174     } else {
175       ec.popObject();
176       // Now let us attach the component
177       switch (actionData.aggregationType) {
178       case AS_COMPLEX_PROPERTY:
179         actionData.parentBean.setComplexProperty(tagName, actionData
180             .getNestedComplexProperty());
181 
182         break;
183       case AS_COMPLEX_PROPERTY_COLLECTION:
184         actionData.parentBean.addComplexProperty(tagName, actionData
185             .getNestedComplexProperty());
186 
187         break;
188       }
189     }
190   }
191 
192 }