Coverage Report - org.apache.commons.flatfile.morph.BaseEntityCollectionReflector
 
Classes in this File Line Coverage Branch Coverage Complexity
BaseEntityCollectionReflector
76%
32/42
75%
12/16
0
BaseEntityCollectionReflector$DefaultPropertyAccessor
100%
7/7
100%
4/4
0
BaseEntityCollectionReflector$OverridingPropertyAccessor
57%
4/7
N/A
0
BaseEntityCollectionReflector$PropertyAccessor
100%
15/15
86%
12/14
0
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to You under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *      http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 package org.apache.commons.flatfile.morph;
 18  
 
 19  
 import java.util.Arrays;
 20  
 import java.util.HashSet;
 21  
 import java.util.Iterator;
 22  
 
 23  
 import org.apache.commons.flatfile.Entity;
 24  
 import org.apache.commons.flatfile.EntityCollection;
 25  
 
 26  
 import net.sf.morph.Defaults;
 27  
 import net.sf.morph.reflect.IndexedContainerReflector;
 28  
 import net.sf.morph.reflect.reflectors.BaseContainerReflector;
 29  
 import net.sf.morph.reflect.reflectors.ObjectReflector;
 30  
 import net.sf.morph.transform.DecoratedConverter;
 31  
 import net.sf.morph.util.ClassUtils;
 32  
 
 33  
 /**
 34  
  * Base EntityCollection reflector.
 35  
  * @version $Revision: 760708 $ $Date: 2009-03-31 17:54:04 -0500 (Tue, 31 Mar 2009) $
 36  
  */
 37  376
 public abstract class BaseEntityCollectionReflector extends BaseContainerReflector {
 38  
     /**
 39  
      * Common property accessor logic.
 40  
      */
 41  
     private abstract class PropertyAccessor {
 42  
         /**
 43  
          * Property name to find
 44  
          */
 45  
         protected String propertyName;
 46  
 
 47  
         /**
 48  
          * Create a new PropertyStrategy instance.
 49  
          * @param propertyName to find
 50  
          */
 51  827
         protected PropertyAccessor(String propertyName) {
 52  827
             this.propertyName = propertyName;
 53  827
         }
 54  
 
 55  
         /**
 56  
          * Get the property type of <code>bean</code>.
 57  
          * @param bean to inspect
 58  
          * @return Class<?>
 59  
          * @throws Exception on error
 60  
          */
 61  
         final Class<?> getTypeFrom(Object bean) throws Exception {
 62  155
             if (isEntity(bean, propertyName)) {
 63  91
                 return getEntityType(getEntity(bean, propertyName));
 64  
             }
 65  64
             return FALLBACK.getType(bean, propertyName);
 66  
         }
 67  
 
 68  
         /**
 69  
          * Strategy template method.
 70  
          * @param entity to inspect
 71  
          * @return Class<?>
 72  
          * @throws Exception on error
 73  
          */
 74  
         abstract Class<?> getEntityType(Entity entity) throws Exception;
 75  
 
 76  
         /**
 77  
          * Read the property.
 78  
          * @param bean to read
 79  
          * @return value
 80  
          * @throws Exception on error
 81  
          */
 82  
         final Object getFrom(Object bean) throws Exception {
 83  148
             if (isEntity(bean, propertyName)) {
 84  98
                 return getEntityValue(getEntity(bean, propertyName));
 85  
             }
 86  50
             return FALLBACK.get(bean, propertyName);
 87  
         }
 88  
 
 89  
         /**
 90  
          * Get the value of the specified Entity.
 91  
          * @param entity to read
 92  
          * @return Object value
 93  
          * @throws Exception on error
 94  
          */
 95  
         abstract Object getEntityValue(Entity entity) throws Exception;
 96  
 
 97  
         /**
 98  
          * Set the property.
 99  
          * @param bean target
 100  
          * @param propertyValue to set
 101  
          * @throws Exception on error
 102  
          */
 103  
         final void setOn(Object bean, Object propertyValue) throws Exception {
 104  38
             if (isEntity(bean, propertyName)) {
 105  22
                 setEntityValueOn(bean, getEntity(bean, propertyName), propertyValue);
 106  
             } else {
 107  16
                 FALLBACK.set(bean, propertyName, propertyValue);
 108  
             }
 109  38
         }
 110  
 
 111  
         /**
 112  
          * Set the entity value.
 113  
          * @param bean target
 114  
          * @param e Entity
 115  
          * @param propertyValue value
 116  
          * @throws Exception on error
 117  
          */
 118  
         abstract void setEntityValueOn(Object bean, Entity e, Object propertyValue)
 119  
                 throws Exception;
 120  
 
 121  
         /**
 122  
          * Learn whether our property can be read from a given target.
 123  
          * @param bean to inspect
 124  
          * @return boolean
 125  
          * @throws Exception on error
 126  
          */
 127  
         final boolean isReadableOn(Object bean) throws Exception {
 128  351
             return isEntity(bean, propertyName) || FALLBACK.isReadable(bean, propertyName);
 129  
         }
 130  
 
 131  
         /**
 132  
          * Learn whether our property can be written to a given target.
 133  
          * @param bean to inspect
 134  
          * @return boolean
 135  
          * @throws Exception on error
 136  
          */
 137  
         final boolean isWriteableOn(Object bean) throws Exception {
 138  135
             return isEntity(bean, propertyName) || FALLBACK.isWriteable(bean, propertyName);
 139  
         }
 140  
     }
 141  
 
 142  
     /**
 143  
      * Overrides the default strategy to treat an Entity as an ordinary javabean.
 144  
      */
 145  
     private class OverridingPropertyAccessor extends PropertyAccessor {
 146  
         /**
 147  
          * Create a new OverridingPropertyAccessor instance.
 148  
          * @param propertyName String
 149  
          */
 150  32
         public OverridingPropertyAccessor(String propertyName) {
 151  32
             super(propertyName.substring(TYPE_OVERRIDE.length()));
 152  32
         }
 153  
 
 154  
         /**
 155  
          * {@inheritDoc}
 156  
          */
 157  
         Class<?> getEntityType(Entity e) {
 158  0
             return ClassUtils.getClass(e);
 159  
         }
 160  
 
 161  
         /**
 162  
          * {@inheritDoc}
 163  
          */
 164  
         Object getEntityValue(Entity e) {
 165  8
             return e;
 166  
         }
 167  
 
 168  
         /**
 169  
          * {@inheritDoc}
 170  
          */
 171  
         void setEntityValueOn(Object bean, Entity e, Object propertyValue) throws Exception {
 172  0
             FALLBACK.set(bean, propertyName, propertyValue);
 173  0
         }
 174  
     }
 175  
 
 176  
     /**
 177  
      * Default {@link PropertyAccessor}
 178  
      */
 179  
     private class DefaultPropertyAccessor extends PropertyAccessor {
 180  
         /**
 181  
          * Create a new DefaultPropertyAccessor instance.
 182  
          * @param propertyName String
 183  
          */
 184  795
         public DefaultPropertyAccessor(String propertyName) {
 185  795
             super(propertyName);
 186  795
         }
 187  
 
 188  
         /**
 189  
          * {@inheritDoc}
 190  
          */
 191  
         Class<?> getEntityType(Entity e) {
 192  91
             return e instanceof EntityCollection ? EntityCollection.class : String.class;
 193  
         }
 194  
 
 195  
         /**
 196  
          * {@inheritDoc}
 197  
          */
 198  
         Object getEntityValue(Entity e) {
 199  90
             return e instanceof EntityCollection ? (Object) e : getString(e);
 200  
         }
 201  
 
 202  
         /**
 203  
          * {@inheritDoc}
 204  
          */
 205  
         void setEntityValueOn(Object bean, Entity e, Object propertyValue) throws Exception {
 206  22
             setEntityValue(e, propertyValue);
 207  22
         }
 208  
     }
 209  
 
 210  
     /** Property name prefix indicating to treat Entities as beans. */
 211  
     public static final String TYPE_OVERRIDE = "@";
 212  
 
 213  1
     private static final ObjectReflector FALLBACK = new ObjectReflector();
 214  
 
 215  
     private DecoratedConverter toTextConverter;
 216  12
     private boolean trimStrings = true;
 217  
 
 218  
     /**
 219  
      * Get the boolean trimStrings.
 220  
      * @return boolean
 221  
      */
 222  
     public boolean isTrimStrings() {
 223  84
         return trimStrings;
 224  
     }
 225  
 
 226  
     /**
 227  
      * Set the boolean trimStrings.
 228  
      * @param trimStrings boolean
 229  
      */
 230  
     public void setTrimStrings(boolean trimStrings) {
 231  0
         this.trimStrings = trimStrings;
 232  0
     }
 233  
 
 234  
     /**
 235  
      * {@inheritDoc}
 236  
      */
 237  
     @SuppressWarnings("unchecked")
 238  
     protected Class<?> getContainedTypeImpl(Class c) throws Exception {
 239  3
         return Entity.class;
 240  
     }
 241  
 
 242  
     /**
 243  
      * {@inheritDoc}
 244  
      */
 245  
     protected Iterator<Entity> getIteratorImpl(Object o) throws Exception {
 246  0
         return ((EntityCollection) o).getChildren().iterator();
 247  
     }
 248  
 
 249  
     /**
 250  
      * {@inheritDoc}
 251  
      */
 252  
     protected int getSizeImpl(Object o) throws Exception {
 253  0
         return ((EntityCollection) o).getChildren().size();
 254  
     }
 255  
 
 256  
     /**
 257  
      * {@inheritDoc}
 258  
      */
 259  
     protected Class<?> getTypeImpl(Object bean, String propertyName) throws Exception {
 260  155
         return getPropertyAccessor(propertyName).getTypeFrom(bean);
 261  
     }
 262  
 
 263  
     /**
 264  
      * {@inheritDoc}
 265  
      */
 266  
     protected Object getImpl(Object container, int index) throws Exception {
 267  6
         Entity e = getEntity(container, index);
 268  6
         return e instanceof EntityCollection ? (Object) e : getString(e);
 269  
     }
 270  
 
 271  
     /**
 272  
      * {@inheritDoc}
 273  
      */
 274  
     protected Object getImpl(Object bean, String propertyName) throws Exception {
 275  148
         return getPropertyAccessor(propertyName).getFrom(bean);
 276  
     }
 277  
 
 278  
     /**
 279  
      * {@inheritDoc}
 280  
      */
 281  
     protected void setImpl(Object bean, String propertyName, Object value) throws Exception {
 282  38
         getPropertyAccessor(propertyName).setOn(bean, value);
 283  38
     }
 284  
 
 285  
     /**
 286  
      * {@inheritDoc}
 287  
      */
 288  
     protected Object setImpl(Object container, int index, Object propertyValue) throws Exception {
 289  0
         Entity e = getEntity(container, index);
 290  0
         byte[] result = e.getValue();
 291  0
         setEntityValue(e, propertyValue);
 292  0
         return result;
 293  
     }
 294  
 
 295  
     /**
 296  
      * Set the value of an Entity
 297  
      * @param value Object Set the value of <code>e</code> by converting <code>value</code> to a byte[]
 298  
      * @param e Entity
 299  
      * @throws Exception on error
 300  
      */
 301  
     protected void setEntityValue(Entity e, Object value) throws Exception {
 302  22
         e.setValue((byte[]) getToTextConverter().convert(byte[].class, value));
 303  22
     }
 304  
 
 305  
     /**
 306  
      * {@inheritDoc}
 307  
      */
 308  
     protected String[] getPropertyNamesImpl(Object bean) throws Exception {
 309  354
         HashSet<String> result = new HashSet<String>();
 310  354
         if (this instanceof IndexedContainerReflector) {
 311  81
             result.addAll(Arrays.asList(super.getPropertyNamesImpl(bean)));
 312  
         }
 313  354
         String[] fallback = FALLBACK.getPropertyNames(bean);
 314  354
         if (fallback != null && fallback.length > 0) {
 315  354
             result.addAll(Arrays.asList(fallback));
 316  
             // add override property names:
 317  3237
             for (int i = 0; i < fallback.length; i++) {
 318  2883
                 result.add("@" + fallback[i]);
 319  
             }
 320  
         }
 321  354
         return result.toArray(new String[result.size()]);
 322  
     }
 323  
 
 324  
     /**
 325  
      * Learn whether the specified property refers to an Entity child.
 326  
      * @param bean to inspect
 327  
      * @param propertyName to read
 328  
      * @return boolean
 329  
      * @throws Exception on error
 330  
      */
 331  
     protected abstract boolean isEntity(Object bean, String propertyName) throws Exception;
 332  
 
 333  
     /**
 334  
      * Get the named child entity.
 335  
      * @param bean to read
 336  
      * @param propertyName to read
 337  
      * @return Entity
 338  
      * @throws Exception on error
 339  
      */
 340  
     protected Entity getEntity(Object bean, String propertyName) throws Exception {
 341  0
         return getEntity(bean, Integer.parseInt(propertyName));
 342  
     }
 343  
 
 344  
     /**
 345  
      * Get the child entity at index <code>index</code>.
 346  
      * Base implementation throws {@link UnsupportedOperationException}.
 347  
      * @param container to read
 348  
      * @param index to get
 349  
      * @return Entity
 350  
      * @throws Exception on error
 351  
      */
 352  
     protected Entity getEntity(Object container, int index) throws Exception {
 353  0
         throw new UnsupportedOperationException();
 354  
     }
 355  
 
 356  
     /**
 357  
      * {@inheritDoc}
 358  
      */
 359  
     protected boolean isReadableImpl(Object bean, String propertyName) throws Exception {
 360  351
         return getPropertyAccessor(propertyName).isReadableOn(bean);
 361  
     }
 362  
 
 363  
     /**
 364  
      * {@inheritDoc}
 365  
      */
 366  
     protected boolean isWriteableImpl(Object bean, String propertyName) throws Exception {
 367  135
         return getPropertyAccessor(propertyName).isWriteableOn(bean);
 368  
     }
 369  
 
 370  
     /**
 371  
      * Get the to-text {@link DecoratedConverter} assigned.
 372  
      * @return the toTextConverter
 373  
      */
 374  
     public synchronized DecoratedConverter getToTextConverter() {
 375  22
         if (toTextConverter == null) {
 376  4
             setToTextConverter(Defaults.createToTextConverter());
 377  
         }
 378  22
         return toTextConverter;
 379  
     }
 380  
 
 381  
     /**
 382  
      * Set the to-text {@link DecoratedConverter}.
 383  
      * @param toTextConverter to set
 384  
      */
 385  
     public synchronized void setToTextConverter(DecoratedConverter toTextConverter) {
 386  4
         this.toTextConverter = toTextConverter;
 387  4
     }
 388  
 
 389  
     /**
 390  
      * Get a PropertyAccessor instance to handle this property.
 391  
      * @param propertyName to handle
 392  
      * @return PropertyStrategy
 393  
      */
 394  
     private PropertyAccessor getPropertyAccessor(String propertyName) {
 395  827
         return propertyName.startsWith(TYPE_OVERRIDE) ? (PropertyAccessor) new OverridingPropertyAccessor(
 396  
                 propertyName)
 397  
                 : new DefaultPropertyAccessor(propertyName);
 398  
     }
 399  
 
 400  
     /**
 401  
      * Get the value of the specified Entity as a String, respecting this {@link BaseEntityCollectionReflector}'s
 402  
      * trimStrings property.
 403  
      * @param e Entity
 404  
      * @return e's value as a String.
 405  
      */
 406  
     private String getString(Entity e) {
 407  84
         String s = new String(e.getValue());
 408  84
         return isTrimStrings() ? s.trim() : s;
 409  
     }
 410  
 
 411  
 }