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