Coverage Report - org.apache.commons.flatfile.morph.BaseEntityCollectionReflector
 
Classes in this File Line Coverage Branch Coverage Complexity
BaseEntityCollectionReflector
77%
35/45
75%
12/16
1.474
BaseEntityCollectionReflector$DefaultPropertyAccessor
100%
7/7
100%
4/4
1.474
BaseEntityCollectionReflector$OverridingPropertyAccessor
57%
4/7
N/A
1.474
BaseEntityCollectionReflector$PropertyAccessor
100%
16/16
100%
14/14
1.474
 
 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: 1301237 $ $Date: 2012-03-15 17:08:23 -0500 (Thu, 15 Mar 2012) $
 36  
  */
 37  1552
 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  4962
         protected PropertyAccessor(String propertyName) {
 52  4962
             this.propertyName = propertyName;
 53  4962
         }
 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  930
             if (isEntity(bean, propertyName)) {
 63  546
                 return getEntityType(getEntity(bean, propertyName));
 64  
             }
 65  384
             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  888
             if (isEntity(bean, propertyName)) {
 84  588
                 return getEntityValue(getEntity(bean, propertyName));
 85  
             }
 86  300
             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  228
             if (isEntity(bean, propertyName)) {
 105  132
                 setEntityValueOn(bean, getEntity(bean, propertyName), propertyValue);
 106  44
             } else {
 107  96
                 FALLBACK.set(bean, propertyName, propertyValue);
 108  
             }
 109  228
         }
 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  2106
             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  810
             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  192
         public OverridingPropertyAccessor(String propertyName) {
 151  192
             super(propertyName.substring(TYPE_OVERRIDE.length()));
 152  192
         }
 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  48
             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  4770
         public DefaultPropertyAccessor(String propertyName) {
 185  4770
             super(propertyName);
 186  4770
         }
 187  
 
 188  
         /**
 189  
          * {@inheritDoc}
 190  
          */
 191  
         Class<?> getEntityType(Entity e) {
 192  546
             return e instanceof EntityCollection ? EntityCollection.class : String.class;
 193  
         }
 194  
 
 195  
         /**
 196  
          * {@inheritDoc}
 197  
          */
 198  
         Object getEntityValue(Entity e) {
 199  540
             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  132
             setEntityValue(e, propertyValue);
 207  132
         }
 208  
     }
 209  
 
 210  
     /** Property name prefix indicating to treat Entities as beans. */
 211  
     public static final String TYPE_OVERRIDE = "@";
 212  
 
 213  566
     private static final ObjectReflector FALLBACK = new ObjectReflector();
 214  
 
 215  
     private DecoratedConverter toTextConverter;
 216  72
     private boolean trimStrings = true;
 217  
 
 218  
     /**
 219  
      * Get the boolean trimStrings.
 220  
      * @return boolean
 221  
      */
 222  
     public boolean isTrimStrings() {
 223  504
         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  
     @Override
 238  
     protected Class<?> getContainedTypeImpl(@SuppressWarnings("rawtypes") Class c) throws Exception {
 239  18
         return Entity.class;
 240  
     }
 241  
 
 242  
     /**
 243  
      * {@inheritDoc}
 244  
      */
 245  
     @Override
 246  
     protected Iterator<Entity> getIteratorImpl(Object o) throws Exception {
 247  0
         return ((EntityCollection) o).getChildren().iterator();
 248  
     }
 249  
 
 250  
     /**
 251  
      * {@inheritDoc}
 252  
      */
 253  
     @Override
 254  
     protected int getSizeImpl(Object o) throws Exception {
 255  0
         return ((EntityCollection) o).getChildren().size();
 256  
     }
 257  
 
 258  
     /**
 259  
      * {@inheritDoc}
 260  
      */
 261  
     @Override
 262  
     protected Class<?> getTypeImpl(Object bean, String propertyName) throws Exception {
 263  930
         return getPropertyAccessor(propertyName).getTypeFrom(bean);
 264  
     }
 265  
 
 266  
     /**
 267  
      * {@inheritDoc}
 268  
      */
 269  
     @Override
 270  
     protected Object getImpl(Object container, int index) throws Exception {
 271  36
         Entity e = getEntity(container, index);
 272  36
         return e instanceof EntityCollection ? (Object) e : getString(e);
 273  
     }
 274  
 
 275  
     /**
 276  
      * {@inheritDoc}
 277  
      */
 278  
     @Override
 279  
     protected Object getImpl(Object bean, String propertyName) throws Exception {
 280  888
         return getPropertyAccessor(propertyName).getFrom(bean);
 281  
     }
 282  
 
 283  
     /**
 284  
      * {@inheritDoc}
 285  
      */
 286  
     @Override
 287  
     protected void setImpl(Object bean, String propertyName, Object value) throws Exception {
 288  228
         getPropertyAccessor(propertyName).setOn(bean, value);
 289  228
     }
 290  
 
 291  
     /**
 292  
      * {@inheritDoc}
 293  
      */
 294  
     @Override
 295  
     protected Object setImpl(Object container, int index, Object propertyValue) throws Exception {
 296  0
         Entity e = getEntity(container, index);
 297  0
         byte[] result = e.getValue();
 298  0
         setEntityValue(e, propertyValue);
 299  0
         return result;
 300  
     }
 301  
 
 302  
     /**
 303  
      * Set the value of an Entity
 304  
      * @param value Object Set the value of <code>e</code> by converting <code>value</code> to a byte[]
 305  
      * @param e Entity
 306  
      * @throws Exception on error
 307  
      */
 308  
     protected void setEntityValue(Entity e, Object value) throws Exception {
 309  132
         e.setValue((byte[]) getToTextConverter().convert(byte[].class, value));
 310  132
     }
 311  
 
 312  
     /**
 313  
      * {@inheritDoc}
 314  
      */
 315  
     @Override
 316  
     protected String[] getPropertyNamesImpl(Object bean) throws Exception {
 317  2124
         HashSet<String> result = new HashSet<String>();
 318  2124
         if (this instanceof IndexedContainerReflector) {
 319  486
             result.addAll(Arrays.asList(super.getPropertyNamesImpl(bean)));
 320  
         }
 321  2124
         String[] fallback = FALLBACK.getPropertyNames(bean);
 322  2124
         if (fallback != null && fallback.length > 0) {
 323  2124
             result.addAll(Arrays.asList(fallback));
 324  
             // add override property names:
 325  19422
             for (int i = 0; i < fallback.length; i++) {
 326  17298
                 result.add("@" + fallback[i]);
 327  
             }
 328  
         }
 329  2124
         return result.toArray(new String[result.size()]);
 330  
     }
 331  
 
 332  
     /**
 333  
      * Learn whether the specified property refers to an Entity child.
 334  
      * @param bean to inspect
 335  
      * @param propertyName to read
 336  
      * @return boolean
 337  
      * @throws Exception on error
 338  
      */
 339  
     protected abstract boolean isEntity(Object bean, String propertyName) throws Exception;
 340  
 
 341  
     /**
 342  
      * Get the named child entity.
 343  
      * @param bean to read
 344  
      * @param propertyName to read
 345  
      * @return Entity
 346  
      * @throws Exception on error
 347  
      */
 348  
     protected Entity getEntity(Object bean, String propertyName) throws Exception {
 349  0
         return getEntity(bean, Integer.parseInt(propertyName));
 350  
     }
 351  
 
 352  
     /**
 353  
      * Get the child entity at index <code>index</code>.
 354  
      * Base implementation throws {@link UnsupportedOperationException}.
 355  
      * @param container to read
 356  
      * @param index to get
 357  
      * @return Entity
 358  
      * @throws Exception on error
 359  
      */
 360  
     protected Entity getEntity(Object container, int index) throws Exception {
 361  0
         throw new UnsupportedOperationException();
 362  
     }
 363  
 
 364  
     /**
 365  
      * {@inheritDoc}
 366  
      */
 367  
     @Override
 368  
     protected boolean isReadableImpl(Object bean, String propertyName) throws Exception {
 369  2106
         return getPropertyAccessor(propertyName).isReadableOn(bean);
 370  
     }
 371  
 
 372  
     /**
 373  
      * {@inheritDoc}
 374  
      */
 375  
     @Override
 376  
     protected boolean isWriteableImpl(Object bean, String propertyName) throws Exception {
 377  810
         return getPropertyAccessor(propertyName).isWriteableOn(bean);
 378  
     }
 379  
 
 380  
     /**
 381  
      * Get the to-text {@link DecoratedConverter} assigned.
 382  
      * @return the toTextConverter
 383  
      */
 384  
     public synchronized DecoratedConverter getToTextConverter() {
 385  132
         if (toTextConverter == null) {
 386  24
             setToTextConverter(Defaults.createToTextConverter());
 387  
         }
 388  132
         return toTextConverter;
 389  
     }
 390  
 
 391  
     /**
 392  
      * Set the to-text {@link DecoratedConverter}.
 393  
      * @param toTextConverter to set
 394  
      */
 395  
     public synchronized void setToTextConverter(DecoratedConverter toTextConverter) {
 396  24
         this.toTextConverter = toTextConverter;
 397  24
     }
 398  
 
 399  
     /**
 400  
      * Get a PropertyAccessor instance to handle this property.
 401  
      * @param propertyName to handle
 402  
      * @return PropertyStrategy
 403  
      */
 404  
     private PropertyAccessor getPropertyAccessor(String propertyName) {
 405  6616
         return propertyName.startsWith(TYPE_OVERRIDE) ? (PropertyAccessor) new OverridingPropertyAccessor(
 406  64
                 propertyName)
 407  1590
                 : new DefaultPropertyAccessor(propertyName);
 408  
     }
 409  
 
 410  
     /**
 411  
      * Get the value of the specified Entity as a String, respecting this {@link BaseEntityCollectionReflector}'s
 412  
      * trimStrings property.
 413  
      * @param e Entity
 414  
      * @return e's value as a String.
 415  
      */
 416  168
     private String getString(Entity e) {
 417  504
         String s = new String(e.getValue());
 418  504
         return isTrimStrings() ? s.trim() : s;
 419  
     }
 420  
 
 421  
 }