Coverage Report - org.apache.commons.betwixt.io.BeanRuleSet

Classes in this File Line Coverage Branch Coverage Complexity
BeanRuleSet
51% 
78% 
1.409

 1  
 /*
 2  
  * Copyright 2001-2004 The Apache Software Foundation.
 3  
  * 
 4  
  * Licensed under the Apache License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  * 
 8  
  *      http://www.apache.org/licenses/LICENSE-2.0
 9  
  * 
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */ 
 16  
 
 17  
 package org.apache.commons.betwixt.io;
 18  
 
 19  
 import org.apache.commons.betwixt.BindingConfiguration;
 20  
 import org.apache.commons.betwixt.ElementDescriptor;
 21  
 import org.apache.commons.betwixt.XMLIntrospector;
 22  
 import org.apache.commons.betwixt.expression.Context;
 23  
 import org.apache.commons.betwixt.io.read.BeanBindAction;
 24  
 import org.apache.commons.betwixt.io.read.MappingAction;
 25  
 import org.apache.commons.betwixt.io.read.ReadConfiguration;
 26  
 import org.apache.commons.betwixt.io.read.ReadContext;
 27  
 import org.apache.commons.digester.Digester;
 28  
 import org.apache.commons.digester.Rule;
 29  
 import org.apache.commons.digester.RuleSet;
 30  
 import org.apache.commons.logging.Log;
 31  
 import org.apache.commons.logging.LogFactory;
 32  
 import org.xml.sax.Attributes;
 33  
 
 34  
 /** <p>Sets <code>Betwixt</code> digestion rules for a bean class.</p>
 35  
   *
 36  
   * @author <a href="mailto:rdonkin@apache.org">Robert Burrell Donkin</a>
 37  
   * @author <a href="mailto:martin@mvdb.net">Martin van den Bemt</a>
 38  
   * @since 0.5
 39  
   */
 40  487
 public class BeanRuleSet implements RuleSet {
 41  
 
 42  
     /** Logger */
 43  18164
     private static Log log = LogFactory.getLog(BeanRuleSet.class);
 44  
 
 45  
     /** 
 46  
     * Set log to be used by <code>BeanRuleSet</code> instances 
 47  
     * @param aLog the <code>Log</code> implementation for this class to log to
 48  
     */
 49  
     public static void setLog(Log aLog) {
 50  0
         log = aLog;
 51  0
     }
 52  
 
 53  
     /** The base path under which the rules will be attached */
 54  
     private String basePath;
 55  
     /** The element descriptor for the base  */
 56  
     private ElementDescriptor baseElementDescriptor;
 57  
     /** The (empty) base context from which all Contexts 
 58  
     with beans are (directly or indirectly) obtained */
 59  63337
     private DigesterReadContext context;
 60  
     /** allows an attribute to be specified to overload the types of beans used */
 61  1539
     private String classNameAttribute = "className";
 62  
 
 63  
     /**
 64  
      * Base constructor.
 65  
      *
 66  
      * @param introspector the <code>XMLIntrospector</code> used to introspect 
 67  
      * @param basePath specifies the (Digester-style) path under which the rules will be attached
 68  
      * @param baseElementDescriptor the <code>ElementDescriptor</code> used to create the rules
 69  
      * @param baseBeanClass the <code>Class</code> whose mapping rules will be created
 70  
      * @param matchIDs should ID/IDREFs be used to match beans?
 71  
      * @deprecated 0.5 use constructor which takes a ReadContext instead
 72  
      */
 73  0
     public BeanRuleSet(
 74  
         XMLIntrospector introspector,
 75  
         String basePath,
 76  
         ElementDescriptor baseElementDescriptor,
 77  
         Class baseBeanClass,
 78  
         boolean matchIDs) {
 79  0
         this.basePath = basePath;
 80  0
         this.baseElementDescriptor = baseElementDescriptor;
 81  0
         BindingConfiguration bindingConfiguration = new BindingConfiguration();
 82  0
         bindingConfiguration.setMapIDs(matchIDs);
 83  0
         context =
 84  0
             new DigesterReadContext(
 85  0
                 log,
 86  0
                 bindingConfiguration,
 87  0
                 new ReadConfiguration());
 88  0
         context.setRootClass(baseBeanClass);
 89  0
         context.setXMLIntrospector(introspector);
 90  0
     }
 91  
 
 92  
     /**
 93  
      * Base constructor.
 94  
      *
 95  
      * @param introspector the <code>XMLIntrospector</code> used to introspect 
 96  
      * @param basePath specifies the (Digester-style) path under which the rules will be attached
 97  
      * @param baseElementDescriptor the <code>ElementDescriptor</code> used to create the rules
 98  
      * @param context the root Context that bean carrying Contexts should be obtained from, 
 99  
      * not null
 100  
      * @deprecated 0.6 use the constructor which takes a ReadContext instead
 101  
      */
 102  0
     public BeanRuleSet(
 103  
         XMLIntrospector introspector,
 104  
         String basePath,
 105  
         ElementDescriptor baseElementDescriptor,
 106  
         Context context) {
 107  
 
 108  0
         this.basePath = basePath;
 109  0
         this.baseElementDescriptor = baseElementDescriptor;
 110  0
         this.context =
 111  0
             new DigesterReadContext(context, new ReadConfiguration());
 112  0
         this.context.setRootClass(
 113  0
             baseElementDescriptor.getSingularPropertyType());
 114  0
         this.context.setXMLIntrospector(introspector);
 115  0
     }
 116  
 
 117  
     /**
 118  
      * Base constructor.
 119  
      *
 120  
      * @param introspector the <code>XMLIntrospector</code> used to introspect 
 121  
      * @param basePath specifies the (Digester-style) path under which the rules will be attached
 122  
      * @param baseElementDescriptor the <code>ElementDescriptor</code> used to create the rules
 123  
      * @param baseBeanClass the <code>Class</code> whose mapping rules will be created
 124  
      * @param context the root Context that bean carrying Contexts should be obtained from, 
 125  
      * not null
 126  
      * @deprecated 0.5 use the constructor which takes a ReadContext instead
 127  
      */
 128  
     public BeanRuleSet(
 129  
                         XMLIntrospector introspector,
 130  
                         String basePath, 
 131  
                         ElementDescriptor baseElementDescriptor, 
 132  
                         Class baseBeanClass,
 133  
                         Context context) {
 134  0
         this(
 135  0
             introspector,
 136  0
             basePath,
 137  0
             baseElementDescriptor,
 138  0
             baseBeanClass,
 139  0
             new ReadContext( context, new ReadConfiguration() ));
 140  0
     }
 141  
 
 142  
     /**
 143  
      * Base constructor.
 144  
      *
 145  
      * @param introspector the <code>XMLIntrospector</code> used to introspect 
 146  
      * @param basePath specifies the (Digester-style) path under which the rules will be attached
 147  
      * @param baseElementDescriptor the <code>ElementDescriptor</code> used to create the rules
 148  
      * @param baseBeanClass the <code>Class</code> whose mapping rules will be created
 149  
      * @param baseContext the root Context that bean carrying Contexts should be obtained from, 
 150  
      * not null
 151  
      */
 152  1539
     public BeanRuleSet(
 153  
         XMLIntrospector introspector,
 154  
         String basePath,
 155  
         ElementDescriptor baseElementDescriptor,
 156  
         Class baseBeanClass,
 157  
         ReadContext baseContext) {
 158  1539
         this.basePath = basePath;
 159  1539
         this.baseElementDescriptor = baseElementDescriptor;
 160  1539
         this.context = new DigesterReadContext(baseContext);
 161  1539
         this.context.setRootClass(baseBeanClass);
 162  1539
         this.context.setXMLIntrospector(introspector);
 163  1539
     }
 164  
 
 165  
     /**
 166  
      * The name of the attribute which can be specified in the XML to override the
 167  
      * type of a bean used at a certain point in the schema.
 168  
      *
 169  
      * <p>The default value is 'className'.</p>
 170  
      * 
 171  
      * @return The name of the attribute used to overload the class name of a bean
 172  
      */
 173  
     public String getClassNameAttribute() {
 174  0
         return context.getClassNameAttribute();
 175  
     }
 176  
 
 177  
     /**
 178  
      * Sets the name of the attribute which can be specified in 
 179  
      * the XML to override the type of a bean used at a certain 
 180  
      * point in the schema.
 181  
      *
 182  
      * <p>The default value is 'className'.</p>
 183  
      * 
 184  
      * @param classNameAttribute The name of the attribute used to overload the class name of a bean
 185  
      * @deprecated 0.5 set the <code>ReadContext</code> property instead
 186  
      */
 187  
     public void setClassNameAttribute(String classNameAttribute) {
 188  0
         context.setClassNameAttribute(classNameAttribute);
 189  0
     }
 190  
 
 191  
     //-------------------------------- Ruleset implementation
 192  
 
 193  
     /** 
 194  
      * <p>Gets the namespace associated with this ruleset.</p>
 195  
      *
 196  
      * <p><strong>Note</strong> namespaces are not currently supported.</p>
 197  
      * 
 198  
      * @return null
 199  
      */
 200  
     public String getNamespaceURI() {
 201  1539
         return null;
 202  
     }
 203  
 
 204  
     /**
 205  
      * Add rules for bean to given <code>Digester</code>.
 206  
      *
 207  
      * @param digester the <code>Digester</code> to which the rules for the bean will be added
 208  
      */
 209  
     public void addRuleInstances(Digester digester) {
 210  1539
         if (log.isTraceEnabled()) {
 211  0
             log.trace("Adding rules to:" + digester);
 212  
         }
 213  
 
 214  1539
         context.setDigester(digester);
 215  
 
 216  
         // if the classloader is not set, set to the digester classloader
 217  1539
         if (context.getClassLoader() == null) {
 218  1539
             context.setClassLoader(digester.getClassLoader());
 219  
         }
 220  
 
 221  
         // TODO: need to think about strategy for paths
 222  
         // may need to provide a default path and then allow the user to override
 223  1539
         digester.addRule("!" + basePath + "/*", new ActionMappingRule());
 224  1539
     }
 225  
 
 226  
     /**
 227  
      * Single rule that is used to map all elements.
 228  
      * 
 229  
      * @author <a href='http://jakarta.apache.org/'>Apache Commons Team</a>
 230  
      */
 231  1539
     private final class ActionMappingRule extends Rule {
 232  
 
 233  
         /**
 234  
           * Processes the start of a new <code>Element</code>.
 235  
           * The actual processing is delegated to <code>MappingAction</code>'s.
 236  
           * @see Rule#begin(String, String, Attributes)
 237  
           */
 238  
         public void begin(String namespace, String name, Attributes attributes)
 239  
             throws Exception {
 240  
 
 241  8845
             if (log.isTraceEnabled()) {
 242  0
                 int attributesLength = attributes.getLength();
 243  0
                 if (attributesLength > 0) {
 244  0
                     log.trace("Attributes:");
 245  
                 }
 246  0
                 for (int i = 0, size = attributesLength; i < size; i++) {
 247  0
                     log.trace("Local:" + attributes.getLocalName(i));
 248  0
                     log.trace("URI:" + attributes.getURI(i));
 249  0
                     log.trace("QName:" + attributes.getQName(i));
 250  
                 }
 251  
             }
 252  
 
 253  8845
             context.pushElement(name);
 254  
             
 255  8845
             MappingAction nextAction =
 256  8845
                 nextAction(namespace, name, attributes, context);
 257  
 
 258  8845
             context.pushMappingAction(nextAction);
 259  8845
         }
 260  
 
 261  
         /**
 262  
          * Gets the next action to be executed 
 263  
          * @param namespace the element's namespace, not null
 264  
          * @param name the element name, not null
 265  
          * @param attributes the element's attributes, not null
 266  
          * @param context the <code>ReadContext</code> against which the xml is being mapped.
 267  
          * @return the initialized <code>MappingAction</code>, not null
 268  
          * @throws Exception
 269  
          */
 270  
         private MappingAction nextAction(
 271  
             String namespace,
 272  
             String name,
 273  
             Attributes attributes,
 274  
             ReadContext context)
 275  
             throws Exception {
 276  
                 
 277  8845
             MappingAction result = null;
 278  8845
             MappingAction lastAction = context.currentMappingAction();
 279  8845
             if (lastAction == null)
 280  
             {
 281  942
                 result =  BeanBindAction.INSTANCE;   
 282  
             } else {
 283  
                 
 284  7903
                 result = lastAction.next(namespace, name, attributes, context);
 285  
             }
 286  8845
             return result.begin(namespace, name, attributes, context);
 287  
         }
 288  
 
 289  
 
 290  
 
 291  
         /**
 292  
         * Processes the body text for the current element.
 293  
         * This is delegated to the current <code>MappingAction</code>.
 294  
         * @see Rule#body(String, String, String)
 295  
         */
 296  
         public void body(String namespace, String name, String text)
 297  
             throws Exception {
 298  
 
 299  8832
             if (log.isTraceEnabled()) log.trace("[BRS] Body with text " + text);
 300  8832
             if (digester.getCount() > 0) {
 301  8832
                 MappingAction action = context.currentMappingAction();
 302  8832
                 action.body(text, context);
 303  
             } else {
 304  0
                 log.trace("[BRS] ZERO COUNT");
 305  
             }
 306  8819
         }
 307  
 
 308  
         /**
 309  
         * Process the end of this element.
 310  
         * This is delegated to the current <code>MappingAction</code>.
 311  
         */
 312  
         public void end(String namespace, String name) throws Exception {
 313  
 
 314  8819
             MappingAction action = context.popMappingAction();
 315  8819
             action.end(context);
 316  8819
         }
 317  
 
 318  
         /** 
 319  
          * Tidy up.
 320  
          */
 321  
         public void finish() {
 322  
             //
 323  
             // Clear indexed beans so that we're ready to process next document
 324  
             //
 325  1500
             context.clearBeans();
 326  1500
         }
 327  
 
 328  
     }
 329  
 
 330  
     /**
 331  
      * Specialization of <code>ReadContext</code> when reading from <code>Digester</code>.
 332  
      * @author <a href='http://jakarta.apache.org/'>Apache Commons Team</a>
 333  
      * @version $Revision: 240113 $
 334  
      */
 335  
     private static class DigesterReadContext extends ReadContext {
 336  
 
 337  
         private Digester digester;
 338  
 
 339  
         /**
 340  
          * @param context
 341  
          * @param readConfiguration
 342  
          */
 343  
         public DigesterReadContext(
 344  
             Context context,
 345  
             ReadConfiguration readConfiguration) {
 346  0
             super(context, readConfiguration);
 347  
             // TODO Auto-generated constructor stub
 348  0
         }
 349  
 
 350  
         /**
 351  
          * @param bindingConfiguration
 352  
          * @param readConfiguration
 353  
          */
 354  
         public DigesterReadContext(
 355  
             BindingConfiguration bindingConfiguration,
 356  
             ReadConfiguration readConfiguration) {
 357  0
             super(bindingConfiguration, readConfiguration);
 358  0
         }
 359  
 
 360  
         /**
 361  
          * @param log
 362  
          * @param bindingConfiguration
 363  
          * @param readConfiguration
 364  
          */
 365  
         public DigesterReadContext(
 366  
             Log log,
 367  
             BindingConfiguration bindingConfiguration,
 368  
             ReadConfiguration readConfiguration) {
 369  0
             super(log, bindingConfiguration, readConfiguration);
 370  0
         }
 371  
 
 372  
         /**
 373  
          * @param log
 374  
          * @param bindingConfiguration
 375  
          * @param readConfiguration
 376  
          */
 377  
         public DigesterReadContext(ReadContext readContext) {
 378  1539
             super(readContext);
 379  1539
         }
 380  
 
 381  
         public Digester getDigester() {
 382  
             // TODO: replace with something better
 383  0
             return digester;
 384  
         }
 385  
 
 386  
         public void setDigester(Digester digester) {
 387  
             // TODO: replace once moved to single Rule
 388  1539
             this.digester = digester;
 389  1539
         }
 390  
 
 391  
         /* (non-Javadoc)
 392  
          * @see org.apache.commons.betwixt.io.read.ReadContext#pushBean(java.lang.Object)
 393  
          */
 394  
         public void pushBean(Object bean) {
 395  3489
             super.pushBean(bean);
 396  3489
             digester.push(bean);
 397  3489
         }
 398  
 
 399  
         /* (non-Javadoc)
 400  
          * @see org.apache.commons.betwixt.io.read.ReadContext#putBean(java.lang.Object)
 401  
          */
 402  
         public Object popBean() {
 403  3476
             Object bean = super.popBean();
 404  
             // don't pop the last from the stack
 405  3476
             if (digester.getCount() > 0) {
 406  3476
                 digester.pop();
 407  
             }
 408  3476
             return bean;
 409  
         }
 410  
     }
 411  
 
 412  
 }