rdonkin 2003/08/21 15:42:47 Modified: betwixt/src/java/org/apache/commons/betwixt BindingConfiguration.java betwixt/src/java/org/apache/commons/betwixt/expression Context.java betwixt/src/java/org/apache/commons/betwixt/io BeanReader.java BeanRuleSet.java Log: Refactored read bean creation into chain of independet creators. It can now be extended by users. Revision Changes Path 1.2 +33 -6 jakarta-commons/betwixt/src/java/org/apache/commons/betwixt/BindingConfiguration.java Index: BindingConfiguration.java =================================================================== RCS file: /home/cvs/jakarta-commons/betwixt/src/java/org/apache/commons/betwixt/BindingConfiguration.java,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- BindingConfiguration.java 31 Jul 2003 21:35:42 -0000 1.1 +++ BindingConfiguration.java 21 Aug 2003 22:42:47 -0000 1.2 @@ -90,6 +90,8 @@ /** Converts objects <-> strings */ private ObjectStringConverter objectStringConverter; + private String classNameAttribute = "className"; + /** * Constructs a BindingConfiguration with default properties. */ @@ -118,7 +120,7 @@ /** * Sets the Object <-> String converter. - * @param the ObjectStringConverter to be used, not null + * @param objectStringConverter the ObjectStringConverter to be used, not null */ public void setObjectStringConverter(ObjectStringConverter objectStringConverter) { this.objectStringConverter = objectStringConverter; @@ -144,4 +146,29 @@ public void setMapIDs(boolean mapIDs) { this.mapIDs = mapIDs; } + + /** + * The name of the attribute which can be specified in the XML to override the + * type of a bean used at a certain point in the schema. + * + * <p>The default value is 'className'.</p> + * + * @return The name of the attribute used to overload the class name of a bean + */ + public String getClassNameAttribute() { + return classNameAttribute; + } + + /** + * Sets the name of the attribute which can be specified in + * the XML to override the type of a bean used at a certain + * point in the schema. + * + * <p>The default value is 'className'.</p> + * + * @param classNameAttribute The name of the attribute used to overload the class name of a bean + */ + public void setClassNameAttribute(String classNameAttribute) { + this.classNameAttribute = classNameAttribute; + } } 1.6 +45 -8 jakarta-commons/betwixt/src/java/org/apache/commons/betwixt/expression/Context.java Index: Context.java =================================================================== RCS file: /home/cvs/jakarta-commons/betwixt/src/java/org/apache/commons/betwixt/expression/Context.java,v retrieving revision 1.5 retrieving revision 1.6 diff -u -r1.5 -r1.6 --- Context.java 31 Jul 2003 21:40:58 -0000 1.5 +++ Context.java 21 Aug 2003 22:42:47 -0000 1.6 @@ -127,12 +127,22 @@ * * @param bean evaluate expressions against this bean * @param log log to this logger - * @param converter not null + * @param bindingConfiguration not null */ public Context(Object bean, Log log, BindingConfiguration bindingConfiguration) { this( bean, new HashMap(), log, bindingConfiguration ); } + /** + * Construct a cloned context. + * The constructed context should share bean, variables, log and binding configuration. + * @param context duplicate the attributes of this bean + */ + public Context( Context context ) { + this(context.bean, context.variables, context.log, context.bindingConfiguration); + } + + /** Convenience constructor sets evaluted bean, context variables and log. * * @param bean evaluate expressions against this bean @@ -149,7 +159,7 @@ * @param bean evaluate expressions against this bean * @param variables context variables * @param log log to this logger - * @param converter not null + * @param bindingConfiguration not null */ public Context(Object bean, Map variables, Log log, BindingConfiguration bindingConfiguration) { this.bean = bean; @@ -164,7 +174,9 @@ * @return new Context with new bean but shared variables */ public Context newContext(Object newBean) { - return new Context(newBean, variables, log, bindingConfiguration); + Context context = new Context(this); + context.setBean( newBean ); + return context; } /** @@ -252,5 +264,30 @@ */ public boolean getMapIDs() { return bindingConfiguration.getMapIDs(); + } + + /** + * The name of the attribute which can be specified in the XML to override the + * type of a bean used at a certain point in the schema. + * + * <p>The default value is 'className'.</p> + * + * @return The name of the attribute used to overload the class name of a bean + */ + public String getClassNameAttribute() { + return bindingConfiguration.getClassNameAttribute(); + } + + /** + * Sets the name of the attribute which can be specified in + * the XML to override the type of a bean used at a certain + * point in the schema. + * + * <p>The default value is 'className'.</p> + * + * @param classNameAttribute The name of the attribute used to overload the class name of a bean + */ + public void setClassNameAttribute(String classNameAttribute) { + bindingConfiguration.setClassNameAttribute( classNameAttribute ); } } 1.15 +32 -10 jakarta-commons/betwixt/src/java/org/apache/commons/betwixt/io/BeanReader.java Index: BeanReader.java =================================================================== RCS file: /home/cvs/jakarta-commons/betwixt/src/java/org/apache/commons/betwixt/io/BeanReader.java,v retrieving revision 1.14 retrieving revision 1.15 diff -u -r1.14 -r1.15 --- BeanReader.java 31 Jul 2003 21:40:58 -0000 1.14 +++ BeanReader.java 21 Aug 2003 22:42:47 -0000 1.15 @@ -72,6 +72,9 @@ import org.apache.commons.betwixt.ElementDescriptor; import org.apache.commons.betwixt.XMLBeanInfo; import org.apache.commons.betwixt.XMLIntrospector; +import org.apache.commons.betwixt.io.read.ReadConfiguration; +import org.apache.commons.betwixt.io.read.ReadContext; + import org.apache.commons.digester.Digester; import org.apache.commons.digester.RuleSet; import org.apache.commons.logging.Log; @@ -96,6 +99,8 @@ private Set registeredClasses = new HashSet(); /** Dynamic binding configuration settings */ private BindingConfiguration bindingConfiguration = new BindingConfiguration(); + /** Reading specific configuration settings */ + private ReadConfiguration readConfiguration = new ReadConfiguration(); /** * Construct a new BeanReader with default properties. @@ -328,11 +333,27 @@ /** * Sets the dynamic configuration setting to be used for bean reading. - * @param the BindingConfiguration settings, not null + * @param bindingConfiguration the BindingConfiguration settings, not null */ - public void setBindingConfiguration(BindingConfiguration bindingConfiguration) { + public void setBindingConfiguration( BindingConfiguration bindingConfiguration ) { this.bindingConfiguration = bindingConfiguration; } + + /** + * Gets read specific configuration details. + * @return the ReadConfiguration, not null + */ + public ReadConfiguration getReadConfiguration() { + return readConfiguration; + } + + /** + * Sets the read specific configuration details. + * @param readConfiguration not null + */ + public void setReadConfiguration( ReadConfiguration readConfiguration ) { + this.readConfiguration = readConfiguration; + } // Implementation methods //------------------------------------------------------------------------- @@ -356,15 +377,16 @@ path , elementDescriptor, beanClass, - makeContext( null )); + makeContext()); addRuleSet( ruleSet ); } /** * Factory method for new contexts. * Ensure that they are correctly configured. + * @return the ReadContext created, not null */ - private Context makeContext(Object bean) { - return new Context( bean, log, bindingConfiguration ); + private ReadContext makeContext() { + return new ReadContext( log, bindingConfiguration, readConfiguration ); } } 1.11 +82 -114 jakarta-commons/betwixt/src/java/org/apache/commons/betwixt/io/BeanRuleSet.java Index: BeanRuleSet.java =================================================================== RCS file: /home/cvs/jakarta-commons/betwixt/src/java/org/apache/commons/betwixt/io/BeanRuleSet.java,v retrieving revision 1.10 retrieving revision 1.11 diff -u -r1.10 -r1.11 --- BeanRuleSet.java 11 Aug 2003 23:52:20 -0000 1.10 +++ BeanRuleSet.java 21 Aug 2003 22:42:47 -0000 1.11 @@ -73,9 +73,14 @@ import org.apache.commons.betwixt.XMLBeanInfo; import org.apache.commons.betwixt.XMLIntrospector; import org.apache.commons.betwixt.digester.XMLIntrospectorHelper; -import org.apache.commons.betwixt.expression.Context; import org.apache.commons.betwixt.expression.MethodUpdater; import org.apache.commons.betwixt.expression.Updater; +import org.apache.commons.betwixt.io.read.ReadContext; +import org.apache.commons.betwixt.io.read.ReadConfiguration; +import org.apache.commons.betwixt.io.read.BeanCreationChain; +import org.apache.commons.betwixt.io.read.ElementMapping; + + import org.apache.commons.digester.Rule; import org.apache.commons.digester.Digester; import org.apache.commons.digester.RuleSet; @@ -110,8 +115,9 @@ private ElementDescriptor baseElementDescriptor; /** The bean based */ private Class baseBeanClass; - /** The (empty) base context from which all Contexts with beans are (directly or indirectly) obtained */ - private Context baseContext; + /** The (empty) base context from which all Contexts + with beans are (directly or indirectly) obtained */ + private ReadContext baseContext; /** allows an attribute to be specified to overload the types of beans used */ private String classNameAttribute = "className"; @@ -123,7 +129,7 @@ * @param baseElementDescriptor the <code>ElementDescriptor</code> used to create the rules * @param baseBeanClass the <code>Class</code> whose mapping rules will be created * @param matchIDs should ID/IDREFs be used to match beans? - * @deprecated use constructor which takes a base Context + * @deprecated use constructor which takes a ReadContext instead */ public BeanRuleSet( XMLIntrospector introspector, @@ -137,7 +143,7 @@ this.baseBeanClass = baseBeanClass; BindingConfiguration bindingConfiguration = new BindingConfiguration(); bindingConfiguration.setMapIDs( matchIDs ); - baseContext = new Context(null, log , bindingConfiguration); + baseContext = new ReadContext( log , bindingConfiguration, new ReadConfiguration() ); } /** @@ -149,20 +155,43 @@ * @param baseBeanClass the <code>Class</code> whose mapping rules will be created * @param baseContext the root Context that bean carrying Contexts should be obtained from, * not null + * @deprecated use the constructor which takes a ReadContext instead */ public BeanRuleSet( XMLIntrospector introspector, String basePath, ElementDescriptor baseElementDescriptor, Class baseBeanClass, - Context baseContext) { + Context context) { this.introspector = introspector; this.basePath = basePath; this.baseElementDescriptor = baseElementDescriptor; this.baseBeanClass = baseBeanClass; - this.baseContext = baseContext; + this.baseContext = new ReadContext( context, new ReadConfiguration() ); } + /** + * Base constructor. + * + * @param introspector the <code>XMLIntrospector</code> used to introspect + * @param basePath specifies the (Digester-style) path under which the rules will be attached + * @param baseElementDescriptor the <code>ElementDescriptor</code> used to create the rules + * @param baseBeanClass the <code>Class</code> whose mapping rules will be created + * @param baseContext the root Context that bean carrying Contexts should be obtained from, + * not null + */ + public BeanRuleSet( + XMLIntrospector introspector, + String basePath, + ElementDescriptor baseElementDescriptor, + Class baseBeanClass, + ReadContext baseContext) { + this.introspector = introspector; + this.basePath = basePath; + this.baseElementDescriptor = baseElementDescriptor; + this.baseBeanClass = baseBeanClass; + this.baseContext = baseContext; + } /** * The name of the attribute which can be specified in the XML to override the @@ -173,7 +202,7 @@ * @return The name of the attribute used to overload the class name of a bean */ public String getClassNameAttribute() { - return classNameAttribute; + return baseContext.getClassNameAttribute(); } /** @@ -184,11 +213,11 @@ * <p>The default value is 'className'.</p> * * @param classNameAttribute The name of the attribute used to overload the class name of a bean + * @deprecated set the <code>ReadContext</code> property instead */ public void setClassNameAttribute(String classNameAttribute) { - this.classNameAttribute = classNameAttribute; + baseContext.setClassNameAttribute(classNameAttribute); } - //-------------------------------- Ruleset implementation @@ -212,7 +241,8 @@ if (log.isTraceEnabled()) { log.trace("Adding rules to:" + digester); } - ReadContext readContext = new ReadContext( digester ); + + ReadingContext readContext = new ReadingContext( digester ); } /** @@ -221,9 +251,8 @@ * * <p>When an instance is constructed, rules are created and added to digester.</p> */ - private class ReadContext { - /** The beans created by rules in this context indexed by id */ - private Map beansById = new HashMap(); + private class ReadingContext { + /** The rules in this context indexed by path */ private Map rulesByPath = new HashMap(); @@ -232,10 +261,14 @@ * @param digester the <code>Digester</code> * to which the bean mapping rules will be added */ - ReadContext(Digester digester) { - - BeanRule rule = new BeanRule( basePath + "/" , baseElementDescriptor, baseBeanClass ); - addRule( basePath, rule , baseElementDescriptor, rule.context ); + ReadingContext(Digester digester) { + ReadContext context = new ReadContext( baseContext ); + // if the classloader is not set, set to the digester classloader + if ( context.getClassLoader() == null ) { + context.setClassLoader( digester.getClassLoader() ); + } + BeanRule rule = new BeanRule( basePath + "/" , baseElementDescriptor, baseBeanClass, context ); + addRule( basePath, rule , baseElementDescriptor, context ); if ( log.isDebugEnabled() ) { log.debug( "Added root rule to path: " + basePath + " class: " + baseBeanClass ); @@ -262,7 +295,7 @@ private void addChildRules( String prefix, ElementDescriptor currentDescriptor, - Context context ) { + ReadContext context ) { if (log.isTraceEnabled()) { log.trace("Adding child rules for " + currentDescriptor + "@" + prefix); @@ -425,12 +458,12 @@ * * @param path digester path where this rule will be attached * @param childDescriptor update this <code>ElementDescriptor</code> with the body text - * @param context the <code>Context</code> against which the elements will be evaluated + * @param context the <code>ReadContext</code> against which the elements will be evaluated */ void addPrimitiveTypeRule( String path, final ElementDescriptor childDescriptor, - final Context context) { + final ReadContext context) { Rule rule = new Rule() { public void body(String text) throws Exception { @@ -445,9 +478,9 @@ * * @param path digester path where this rule will be attached * @param elementDescriptor update this <code>ElementDescriptor</code> with the body text - * @param context the <code>Context</code> against which the elements will be evaluated + * @param context the <code>ReadContext</code> against which the elements will be evaluated */ - private void addRule( String path, ElementDescriptor elementDescriptor, Context context ) { + private void addRule( String path, ElementDescriptor elementDescriptor, ReadContext context ) { BeanRule rule = new BeanRule( path + '/', elementDescriptor, context ); addRule( path, rule, elementDescriptor, context ); } @@ -459,14 +492,14 @@ * @param rule the <code>Rule</code> to add * @param elementDescriptor the <code>ElementDescriptor</code> * associated with this rule - * @param context the <code>Context</code> against which the elements + * @param context the <code>ReadContext</code> against which the elements * will be evaluated */ private void addRule( String path, Rule rule, ElementDescriptor elementDescriptor, - Context context) { + ReadContext context) { if ( add( path, rule ) ) { // stop infinite recursion by allowing only one rule per path addChildRules( path + '/', elementDescriptor, context ); @@ -512,7 +545,7 @@ /** The descriptor of this element */ private ElementDescriptor descriptor; /** The Context used when evaluating Updaters */ - private Context context; + private ReadContext context; /** In this begin-end loop did we actually create a new bean */ private boolean createdBean; /** The type of the bean to create */ @@ -527,26 +560,8 @@ * @param descriptor the <code>ElementDescriptor</code> describing the element mapped * @param beanClass the <code>Class</code> to be created */ - public BeanRule( ElementDescriptor descriptor, Class beanClass ) { - this( descriptor.getQualifiedName() + "/", descriptor, beanClass ); - } - - /** - * Construct a rule for given bean at given path. - * - * @param pathPrefix the digester style path - * @param descriptor the <code>ElementDescriptor</code> describing the element mapped - * @param beanClass the <code>Class</code> to be created - */ - public BeanRule( - String pathPrefix, - ElementDescriptor descriptor, - Class beanClass ) { - this( - pathPrefix, - descriptor, - beanClass, - baseContext ); + public BeanRule( ElementDescriptor descriptor, Class beanClass, ReadContext context ) { + this( descriptor.getQualifiedName() + "/", descriptor, beanClass, context ); } /** @@ -559,7 +574,7 @@ public BeanRule( String pathPrefix, ElementDescriptor descriptor, - Context context ) { + ReadContext context ) { this( pathPrefix, descriptor, @@ -579,7 +594,7 @@ String pathPrefix, ElementDescriptor descriptor, Class beanClass, - Context context ) { + ReadContext context ) { this.descriptor = descriptor; this.context = context; this.beanClass = beanClass; @@ -601,7 +616,7 @@ * * @param attributes The attribute list of this element */ - public void begin(Attributes attributes) { + public void begin(String namespace, String name, Attributes attributes) { log.debug( "Called with descriptor: " + descriptor + " propertyType: " + descriptor.getPropertyType() ); @@ -626,7 +641,7 @@ Object instance = null; if ( beanClass != null ) { - instance = createBean(attributes); + instance = createBean( namespace, name, attributes ); if ( instance != null ) { createdBean = true; @@ -690,7 +705,7 @@ // XXX so i'm leaving this till later String id = attributes.getValue( "id" ); if ( id != null ) { - getBeansById().put( id, instance ); + context.putBean( id, instance ); } } } @@ -702,7 +717,7 @@ * * @param text the String comprising all the body text */ - public void body(String text) { + public void body(String namespace, String name, String text) { log.trace("Body with text " + text); if ( digester.getCount() > 0 ) { @@ -767,7 +782,7 @@ // // Clear indexed beans so that we're ready to process next document // - beansById.clear(); + baseContext.clearBeans(); } @@ -780,66 +795,19 @@ * @param attributes the <code>Attributes</code> used to match <code>ID/IDREF</code> * @return the created bean */ - protected Object createBean(Attributes attributes) { - // - // See if we've got an IDREF - // - // XXX This should be customizable but i'm not really convinced by - // XXX the existing system - // XXX maybe it's going to have to change so i'll use 'idref' for nows - // + protected Object createBean( String namespace, String name, Attributes attributes ) { + // todo: recycle element mappings + ElementMapping mapping = new ElementMapping(); + mapping.setType( beanClass ); + mapping.setNamespace( namespace ); + mapping.setName( name ); + mapping.setAttributes( attributes ); - /** - * @todo this is a duplicate of the code in BeanCreateRule - * we should try refactor to some common place - */ - if ( context.getMapIDs() ) { - String idref = attributes.getValue( "idref" ); - if ( idref != null ) { - // XXX need to check up about ordering - // XXX this is a very simple system that assumes that - // XXX id occurs before idrefs - // XXX would need some thought about how to implement a fuller system - log.trace( "Found IDREF" ); - Object bean = getBeansById().get( idref ); - if ( bean != null ) { - if (log.isTraceEnabled()) { - log.trace( "Matched bean " + bean ); - } - return bean; - } - log.trace( "No match found" ); - } - } + Object newInstance = context.getBeanCreationChain().create( mapping, context ); - Class theClass = beanClass; - try { - String className = attributes.getValue(classNameAttribute); - if (className != null) { - // load the class we should instantiate - theClass = getDigester().getClassLoader().loadClass(className); - } - if (log.isTraceEnabled()) { - log.trace( "Creating instance of " + theClass ); - } - return theClass.newInstance(); - - } catch (Exception e) { - log.warn( "Could not create instance of type: " + theClass.getName() ); - log.debug( "Create new instance failed: ", e ); - return null; - } + return newInstance; } - - /** - * Get the map used to index beans (previously read in) by id. - * - * @return map indexing beans created by id - */ - protected Map getBeansById() { - return beansById; - } /** * Return something meaningful for logging.
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]