Coverage Report - org.apache.commons.betwixt.io.read.BeanBindAction

Classes in this File Line Coverage Branch Coverage Complexity
BeanBindAction
84% 
96% 
5.167

 1  
 /*
 2  
  * Copyright 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  
 package org.apache.commons.betwixt.io.read;
 17  
 
 18  
 import java.util.Map;
 19  
 
 20  
 import org.apache.commons.betwixt.AttributeDescriptor;
 21  
 import org.apache.commons.betwixt.ElementDescriptor;
 22  
 import org.apache.commons.betwixt.TextDescriptor;
 23  
 import org.apache.commons.betwixt.XMLBeanInfo;
 24  
 import org.apache.commons.betwixt.expression.Updater;
 25  
 import org.apache.commons.logging.Log;
 26  
 import org.xml.sax.Attributes;
 27  
 
 28  
 /**
 29  
  * Action that creates and binds a new bean instance.
 30  
  * 
 31  
  * @author <a href='http://jakarta.apache.org/'>Apache Commons Team</a>
 32  
  * @version $Revision: 155402 $
 33  
  */
 34  948
 public class BeanBindAction extends MappingAction.Base {
 35  
 
 36  
     /** Singleton instance */
 37  474
     public static final BeanBindAction INSTANCE = new BeanBindAction();
 38  
 
 39  
     /**
 40  
      * Begins a new element which is to be bound to a bean.
 41  
      */
 42  
     public MappingAction begin(
 43  
         String namespace,
 44  
         String name,
 45  
         Attributes attributes,
 46  
         ReadContext context)
 47  
                     throws Exception {
 48  
                         
 49  3832
         Log log = context.getLog();
 50  
 
 51  3832
         ElementDescriptor computedDescriptor = context.getCurrentDescriptor();
 52  
 
 53  3832
         if (log.isTraceEnabled()) {
 54  0
             log.trace("Element Pushed: " + name);
 55  
         }
 56  
 
 57  
         // default to ignoring the current element
 58  3832
         MappingAction action = MappingAction.EMPTY;
 59  
 
 60  3832
         Object instance = null;
 61  3832
         Class beanClass = null;
 62  3832
         if (computedDescriptor == null) {
 63  0
             log.trace("No Descriptor");
 64  
         } else {
 65  3832
             beanClass = computedDescriptor.getSingularPropertyType();
 66  
         }
 67  
         // TODO: this is a bit of a workaround 
 68  
         // need to come up with a better way of doing maps
 69  3832
         if (beanClass != null && !Map.class.isAssignableFrom(beanClass)) {
 70  
 
 71  3663
             instance =
 72  7326
                 createBean(
 73  3663
                     namespace,
 74  3663
                     name,
 75  3663
                     attributes,
 76  3663
                     computedDescriptor,
 77  3663
                     context);
 78  
                     
 79  3663
             if (instance != null) {
 80  3463
                 action = this;
 81  3463
                 if (computedDescriptor.isUseBindTimeTypeForMapping())
 82  
                 {
 83  3411
                     beanClass = instance.getClass();
 84  
                 }
 85  3463
                 context.markClassMap(beanClass);
 86  
 
 87  3463
                 if (log.isTraceEnabled()) {
 88  0
                     log.trace("Marked: " + beanClass);
 89  
                 }
 90  
 
 91  3463
                 context.pushBean(instance);
 92  
 
 93  
                 // if we are a reference to a type we should lookup the original
 94  
                 // as this ElementDescriptor will be 'hollow' 
 95  
                 // and have no child attributes/elements.
 96  
                 // XXX: this should probably be done by the NodeDescriptors...
 97  3463
                 ElementDescriptor typeDescriptor =
 98  3463
                     getElementDescriptor(computedDescriptor, context);
 99  
 
 100  
                 // iterate through all attributes        
 101  3463
                 AttributeDescriptor[] attributeDescriptors =
 102  3463
                     typeDescriptor.getAttributeDescriptors();
 103  3463
                 context.populateAttributes(attributeDescriptors, attributes);
 104  
 
 105  3463
                 if (log.isTraceEnabled()) {
 106  0
                     log.trace("Created bean " + instance);
 107  
                 }
 108  
 
 109  
                 // add bean for ID matching
 110  3463
                 if (context.getMapIDs()) {
 111  
                     // XXX need to support custom ID attribute names
 112  
                     // XXX i have a feeling that the current mechanism might need to change
 113  
                     // XXX so i'm leaving this till later
 114  2873
                     String id = attributes.getValue("id");
 115  2873
                     if (id != null) {
 116  299
                         context.putBean(id, instance);
 117  
                     }
 118  
                 }
 119  
             }
 120  
         }
 121  3832
         return action;
 122  
     }
 123  
 
 124  
 
 125  
     public void body(String text, ReadContext context) throws Exception {
 126  3450
         Log log = context.getLog();
 127  
         // Take the first content descriptor
 128  3450
         ElementDescriptor currentDescriptor = context.getCurrentDescriptor();
 129  3450
         if (currentDescriptor == null) {
 130  0
             if (log.isTraceEnabled()) {
 131  0
                 log.trace("path descriptor is null:");
 132  
             }
 133  
         } else {
 134  3450
             TextDescriptor bodyTextdescriptor =
 135  3450
                 currentDescriptor.getPrimaryBodyTextDescriptor();
 136  3450
             if (bodyTextdescriptor != null) {
 137  26
                 if (log.isTraceEnabled()) {
 138  0
                     log.trace("Setting mixed content for:");
 139  0
                     log.trace(bodyTextdescriptor);
 140  
                 }
 141  26
                 Updater updater = bodyTextdescriptor.getUpdater();
 142  26
                 if (log.isTraceEnabled())
 143  
                 {    
 144  0
                     log.trace("Updating mixed content with:");
 145  0
                     log.trace(updater);
 146  
                 }
 147  26
                 if (updater != null && text != null) {
 148  26
                     updater.update(context, text);
 149  
                 }
 150  
             }
 151  
         }
 152  3450
     }
 153  
 
 154  
     public void end(ReadContext context) throws Exception {
 155  
         // force any setters of the parent bean to be called for this new bean instance
 156  3450
         Object instance = context.popBean();
 157  3450
         update(context, instance);
 158  3450
     }
 159  
 
 160  
     private void update(ReadContext context, Object value) throws Exception {
 161  3450
         Log log = context.getLog();
 162  
 
 163  3450
         Updater updater = context.getCurrentUpdater();
 164  
         
 165  3450
         if ( updater == null ) {
 166  929
             if ( context.getLog().isTraceEnabled() ) {
 167  0
                 context.getLog().trace("No updater for " + context.getCurrentElement());
 168  
             }
 169  
         } else {
 170  2521
             updater.update(context, value);
 171  
         }
 172  
 
 173  3450
         String poppedElement = context.popElement();
 174  3450
     }
 175  
 
 176  
 
 177  
 
 178  
 
 179  
     /** 
 180  
     * Factory method to create new bean instances 
 181  
     *
 182  
     * @param namespace the namespace for the element
 183  
     * @param name the local name
 184  
     * @param attributes the <code>Attributes</code> used to match <code>ID/IDREF</code>
 185  
     * @return the created bean
 186  
     */
 187  
     protected Object createBean(
 188  
         String namespace,
 189  
         String name,
 190  
         Attributes attributes,
 191  
         ElementDescriptor descriptor,
 192  
         ReadContext context) {
 193  
         // TODO: recycle element mappings 
 194  
         // Maybe should move the current mapping into the context
 195  3663
         ElementMapping mapping = new ElementMapping();
 196  3663
         Class beanClass = descriptor.getSingularPropertyType();
 197  3663
         if (beanClass != null && beanClass.isArray()) {
 198  65
             beanClass = beanClass.getComponentType();
 199  
         }
 200  
 
 201  
         // TODO: beanClass can be deduced from descriptor
 202  
         // so this feels a little over-engineered
 203  3663
         mapping.setType(beanClass);
 204  3663
         mapping.setNamespace(namespace);
 205  3663
         mapping.setName(name);
 206  3663
         mapping.setAttributes(attributes);
 207  3663
         mapping.setDescriptor(descriptor);
 208  
 
 209  3663
         Object newInstance =
 210  3663
             context.getBeanCreationChain().create(mapping, context);
 211  
 
 212  3663
         return newInstance;
 213  
     }
 214  
 
 215  
     /** Allows the navigation from a reference to a property object to the 
 216  
     * descriptor defining what the property is. i.e. doing the join from a reference 
 217  
     * to a type to lookup its descriptor.
 218  
     * This could be done automatically by the NodeDescriptors. 
 219  
     * Refer to TODO.txt for more info.
 220  
     *
 221  
     * @param propertyDescriptor find descriptor for property object 
 222  
     * referenced by this descriptor
 223  
     * @return descriptor for the singular property class type referenced.
 224  
     */
 225  
     private ElementDescriptor getElementDescriptor(
 226  
         ElementDescriptor propertyDescriptor,
 227  
         ReadContext context) {
 228  3463
         Log log = context.getLog();
 229  3463
         Class beanClass = propertyDescriptor.getSingularPropertyType();
 230  3463
         if (propertyDescriptor.isUseBindTimeTypeForMapping()) {
 231  
             // use the actual bind time type
 232  3411
             Object current = context.getBean();
 233  3411
             if (current != null) {
 234  3411
                 beanClass = current.getClass();
 235  
             }
 236  
         }
 237  3463
         if (beanClass != null && !Map.class.isAssignableFrom(beanClass)) {
 238  3463
             if (beanClass.isArray()) {
 239  0
                 beanClass = beanClass.getComponentType();
 240  
             }
 241  
             // support for derived beans
 242  
             
 243  
             
 244  3463
             if (log.isTraceEnabled()) {
 245  0
                 log.trace("Filling descriptor for: " + beanClass);
 246  
             }
 247  
             try {
 248  3463
                 XMLBeanInfo xmlInfo =
 249  3463
                     context.getXMLIntrospector().introspect(beanClass);
 250  3463
                 return xmlInfo.getElementDescriptor();
 251  
 
 252  0
             } catch (Exception e) {
 253  0
                 log.warn("Could not introspect class: " + beanClass, e);
 254  
             }
 255  
         }
 256  
         // could not find a better descriptor so use the one we've got
 257  0
         return propertyDescriptor;
 258  
     }
 259  
 
 260  
 }