/** * Logback: the reliable, generic, fast and flexible logging framework. * Copyright (C) 1999-2013, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under * either the terms of the Eclipse Public License v1.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) * * under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation. */ package ch.qos.logback.core.joran.action; import java.util.Stack; import ch.qos.logback.core.joran.spi.ElementPath; import org.xml.sax.Attributes; import ch.qos.logback.core.joran.spi.InterpretationContext; import ch.qos.logback.core.joran.spi.NoAutoStartUtil; import ch.qos.logback.core.joran.util.PropertySetter; import ch.qos.logback.core.spi.ContextAware; import ch.qos.logback.core.spi.LifeCycle; import ch.qos.logback.core.util.AggregationType; import ch.qos.logback.core.util.Loader; import ch.qos.logback.core.util.OptionHelper; /** * This action is responsible for tying together a parent object with a child * element for which there is no explicit rule. * * @author Ceki Gülcü */ public class NestedComplexPropertyIA extends ImplicitAction { // actionDataStack contains ActionData instances // We use a stack of ActionData objects in order to support nested // elements which are handled by the same NestedComplexPropertyIA instance. // We push a ActionData instance in the isApplicable method (if the // action is applicable) and pop it in the end() method. // The XML well-formedness property will guarantee that a push will eventually // be followed by a corresponding pop. Stack<IADataForComplexProperty> actionDataStack = new Stack<IADataForComplexProperty>(); public boolean isApplicable(ElementPath elementPath, Attributes attributes, InterpretationContext ic) { String nestedElementTagName = elementPath.peekLast(); // calling ic.peekObject with an empty stack will throw an exception if (ic.isEmpty()) { return false; } Object o = ic.peekObject(); PropertySetter parentBean = new PropertySetter(o); parentBean.setContext(context); AggregationType aggregationType = parentBean .computeAggregationType(nestedElementTagName); switch (aggregationType) { case NOT_FOUND: case AS_BASIC_PROPERTY: case AS_BASIC_PROPERTY_COLLECTION: return false; // we only push action data if NestComponentIA is applicable case AS_COMPLEX_PROPERTY_COLLECTION: case AS_COMPLEX_PROPERTY: IADataForComplexProperty ad = new IADataForComplexProperty(parentBean, aggregationType, nestedElementTagName); actionDataStack.push(ad); return true; default: addError("PropertySetter.computeAggregationType returned " + aggregationType); return false; } } public void begin(InterpretationContext ec, String localName, Attributes attributes) { // LogLog.debug("in NestComponentIA begin method"); // get the action data object pushed in isApplicable() method call IADataForComplexProperty actionData = (IADataForComplexProperty) actionDataStack .peek(); String className = attributes.getValue(CLASS_ATTRIBUTE); // perform variable name substitution className = ec.subst(className); Class<?> componentClass = null; try { if (!OptionHelper.isEmpty(className)) { componentClass = Loader.loadClass(className, context); } else { // guess class name via implicit rules PropertySetter parentBean = actionData.parentBean; componentClass = parentBean.getClassNameViaImplicitRules(actionData .getComplexPropertyName(), actionData.getAggregationType(), ec .getDefaultNestedComponentRegistry()); } if (componentClass == null) { actionData.inError = true; String errMsg = "Could not find an appropriate class for property [" + localName + "]"; addError(errMsg); return; } if (OptionHelper.isEmpty(className)) { addInfo("Assuming default type [" + componentClass.getName() + "] for [" + localName + "] property"); } actionData.setNestedComplexProperty(componentClass.newInstance()); // pass along the repository if (actionData.getNestedComplexProperty() instanceof ContextAware) { ((ContextAware) actionData.getNestedComplexProperty()) .setContext(this.context); } //addInfo("Pushing component [" + localName // + "] on top of the object stack."); ec.pushObject(actionData.getNestedComplexProperty()); } catch (Exception oops) { actionData.inError = true; String msg = "Could not create component [" + localName + "] of type [" + className + "]"; addError(msg, oops); } } public void end(InterpretationContext ec, String tagName) { // pop the action data object pushed in isApplicable() method call // we assume that each this begin IADataForComplexProperty actionData = (IADataForComplexProperty) actionDataStack .pop(); if (actionData.inError) { return; } PropertySetter nestedBean = new PropertySetter(actionData .getNestedComplexProperty()); nestedBean.setContext(context); // have the nested element point to its parent if possible if (nestedBean.computeAggregationType("parent") == AggregationType.AS_COMPLEX_PROPERTY) { nestedBean.setComplexProperty("parent", actionData.parentBean.getObj()); } // start the nested complex property if it implements LifeCycle and is not // marked with a @NoAutoStart annotation Object nestedComplexProperty = actionData.getNestedComplexProperty(); if (nestedComplexProperty instanceof LifeCycle && NoAutoStartUtil.notMarkedWithNoAutoStart(nestedComplexProperty)) { ((LifeCycle) nestedComplexProperty).start(); } Object o = ec.peekObject(); if (o != actionData.getNestedComplexProperty()) { addError("The object on the top the of the stack is not the component pushed earlier."); } else { ec.popObject(); // Now let us attach the component switch (actionData.aggregationType) { case AS_COMPLEX_PROPERTY: actionData.parentBean.setComplexProperty(tagName, actionData .getNestedComplexProperty()); break; case AS_COMPLEX_PROPERTY_COLLECTION: actionData.parentBean.addComplexProperty(tagName, actionData .getNestedComplexProperty()); break; } } } }