You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by rd...@apache.org on 2003/08/22 00:42:47 UTC

cvs commit: jakarta-commons/betwixt/src/java/org/apache/commons/betwixt/io BeanReader.java BeanRuleSet.java

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 &lt-> 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.