001/**
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 * Copyright (C) 1999-2002, 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 ch.qos.logback.core.Context;
017import ch.qos.logback.core.joran.action.ImcplicitActionDataForBasicProperty;
018import ch.qos.logback.core.joran.action.ImplicitModelData;
019import ch.qos.logback.core.joran.action.ImplicitModelDataForComplexProperty;
020import ch.qos.logback.core.joran.spi.NoAutoStartUtil;
021import ch.qos.logback.core.joran.util.PropertySetter;
022import ch.qos.logback.core.joran.util.beans.BeanDescriptionCache;
023import ch.qos.logback.core.model.ComponentModel;
024import ch.qos.logback.core.model.ImplicitModel;
025import ch.qos.logback.core.model.Model;
026import ch.qos.logback.core.spi.ContextAware;
027import ch.qos.logback.core.spi.LifeCycle;
028import ch.qos.logback.core.util.AggregationType;
029import ch.qos.logback.core.util.Loader;
030import ch.qos.logback.core.util.OptionHelper;
031
032public class ImplicitModelHandler extends ModelHandlerBase {
033
034    private final BeanDescriptionCache beanDescriptionCache;
035    private ImplicitModelData implicitModelData;
036    
037    static final String PARENT_PROPPERTY_KEY = "parent";
038
039    boolean inError = false;
040
041    public ImplicitModelHandler(Context context, BeanDescriptionCache beanDescriptionCache) {
042        super(context);
043        this.beanDescriptionCache = beanDescriptionCache;
044    }
045
046    protected Class<? extends ImplicitModel> getSupportedModelClass() {
047        return ImplicitModel.class;
048    }
049
050    static public ImplicitModelHandler makeInstance(Context context, ModelInterpretationContext mic) {
051        BeanDescriptionCache beanDescriptionCache = mic.getBeanDescriptionCache();
052        return new ImplicitModelHandler(context, beanDescriptionCache);
053    }
054
055    @Override
056    public void handle(ModelInterpretationContext mic, Model model) {
057
058        ImplicitModel implicitModel = (ImplicitModel) model;
059
060        // calling intercon.peekObject with an empty stack will throw an exception
061        if (mic.isObjectStackEmpty()) {
062            inError = true;
063            return;
064        }
065        String nestedElementTagName = implicitModel.getTag();
066
067        Object o = mic.peekObject();
068        PropertySetter parentBean = new PropertySetter(beanDescriptionCache, o);
069        parentBean.setContext(context);
070
071        AggregationType aggregationType = parentBean.computeAggregationType(nestedElementTagName);
072
073//        Stack<ImplicitModelData> actionDataStack = mic.getImplicitModelDataStack();
074
075        switch (aggregationType) {
076        case NOT_FOUND:
077            addWarn("Ignoring unkown property [" + nestedElementTagName + "] in [" + o.getClass().getName() + "]");
078            inError = true;
079            // no point in processing submodels
080            implicitModel.markAsSkipped();
081            return;
082        case AS_BASIC_PROPERTY:
083        case AS_BASIC_PROPERTY_COLLECTION:
084            ImcplicitActionDataForBasicProperty adBasicProperty = new ImcplicitActionDataForBasicProperty(parentBean,
085                    aggregationType, nestedElementTagName);
086            implicitModelData = adBasicProperty;
087            doBasicProperty(mic, model, adBasicProperty);
088            return;
089        // we only push action data if NestComponentIA is applicable
090        case AS_COMPLEX_PROPERTY_COLLECTION:
091        case AS_COMPLEX_PROPERTY:
092            ImplicitModelDataForComplexProperty adComplex = new ImplicitModelDataForComplexProperty(parentBean,
093                    aggregationType, nestedElementTagName);
094            implicitModelData = adComplex;
095            doComplex(mic, implicitModel, adComplex);
096            return;
097        default:
098            addError("PropertySetter.computeAggregationType returned " + aggregationType);
099            return;
100        }
101
102    }
103
104    void doBasicProperty(ModelInterpretationContext interpretationContext, Model model,
105            ImcplicitActionDataForBasicProperty actionData) {
106        String finalBody = interpretationContext.subst(model.getBodyText());
107        // get the action data object pushed in isApplicable() method call
108        // IADataForBasicProperty actionData = (IADataForBasicProperty)
109        // actionDataStack.peek();
110        switch (actionData.aggregationType) {
111        case AS_BASIC_PROPERTY:
112            actionData.parentBean.setProperty(actionData.propertyName, finalBody);
113            break;
114        case AS_BASIC_PROPERTY_COLLECTION:
115            actionData.parentBean.addBasicProperty(actionData.propertyName, finalBody);
116            break;
117        default:
118            addError("Unexpected aggregationType " + actionData.aggregationType);
119        }
120    }
121
122    public void doComplex(ModelInterpretationContext interpretationContext, ComponentModel componentModel,
123            ImplicitModelDataForComplexProperty actionData) {
124
125        String className = componentModel.getClassName();
126        // perform variable name substitution
127        String substClassName = interpretationContext.subst(className);
128
129        String fqcn = interpretationContext.getImport(substClassName);
130
131        Class<?> componentClass = null;
132        try {
133
134            if (!OptionHelper.isNullOrEmpty(fqcn)) {
135                componentClass = Loader.loadClass(fqcn, context);
136            } else {
137                // guess class name via implicit rules
138                PropertySetter parentBean = actionData.parentBean;
139                componentClass = parentBean.getClassNameViaImplicitRules(actionData.propertyName,
140                        actionData.getAggregationType(), interpretationContext.getDefaultNestedComponentRegistry());
141            }
142
143            if (componentClass == null) {
144                actionData.inError = true;
145                String errMsg = "Could not find an appropriate class for property [" + componentModel.getTag() + "]";
146                addError(errMsg);
147                return;
148            }
149
150            if (OptionHelper.isNullOrEmpty(fqcn)) {
151                addInfo("Assuming default type [" + componentClass.getName() + "] for [" + componentModel.getTag()
152                        + "] property");
153            }
154
155            actionData.setNestedComplexProperty(componentClass.getConstructor().newInstance());
156
157            // pass along the repository
158            if (actionData.getNestedComplexProperty() instanceof ContextAware) {
159                ((ContextAware) actionData.getNestedComplexProperty()).setContext(this.context);
160            }
161            // addInfo("Pushing component [" + localName
162            // + "] on top of the object stack.");
163            interpretationContext.pushObject(actionData.getNestedComplexProperty());
164
165        } catch (Exception oops) {
166            actionData.inError = true;
167            String msg = "Could not create component [" + componentModel.getTag() + "] of type [" + fqcn + "]";
168            addError(msg, oops);
169        }
170    }
171
172    @Override
173    public void postHandle(ModelInterpretationContext intercon, Model model) {
174        if (inError) {
175            return;
176        }
177
178        if(implicitModelData == null)
179            return;
180        
181        // the action data can in an incorrect state, in which case we need to 
182        // disengage
183        if(implicitModelData.inError) {
184            return;
185        }
186        if (implicitModelData instanceof ImplicitModelDataForComplexProperty) {
187            postHandleComplex(intercon, model, (ImplicitModelDataForComplexProperty) implicitModelData);
188        }
189
190    }
191
192    private void postHandleComplex(ModelInterpretationContext mic, Model model,
193            ImplicitModelDataForComplexProperty imdComplex) {
194
195        PropertySetter nestedBean = new PropertySetter(beanDescriptionCache,
196                imdComplex.getNestedComplexProperty());
197        nestedBean.setContext(context);
198
199        // have the nested element point to its parent if possible
200        if (nestedBean.computeAggregationType(PARENT_PROPPERTY_KEY) == AggregationType.AS_COMPLEX_PROPERTY) {
201            nestedBean.setComplexProperty(PARENT_PROPPERTY_KEY, imdComplex.parentBean.getObj());
202        }
203
204        // start the nested complex property if it implements LifeCycle and is not
205        // marked with a @NoAutoStart annotation
206        Object nestedComplexProperty = imdComplex.getNestedComplexProperty();
207        if (nestedComplexProperty instanceof LifeCycle
208                && NoAutoStartUtil.notMarkedWithNoAutoStart(nestedComplexProperty)) {
209            ((LifeCycle) nestedComplexProperty).start();
210        }
211
212        Object o = mic.peekObject();
213
214        if (o != imdComplex.getNestedComplexProperty()) {
215            addError("The object on the top the of the stack is not the component pushed earlier.");
216        } else {
217            mic.popObject();
218            // Now let us attach the component
219            switch (imdComplex.aggregationType) {
220            case AS_COMPLEX_PROPERTY:
221                imdComplex.parentBean.setComplexProperty(model.getTag(), imdComplex.getNestedComplexProperty());
222
223                break;
224            case AS_COMPLEX_PROPERTY_COLLECTION:
225                imdComplex.parentBean.addComplexProperty(model.getTag(), imdComplex.getNestedComplexProperty());
226                break;
227            default:
228                addError("Unexpected aggregationType " + imdComplex.aggregationType);
229            }
230        }
231    }
232
233}