Coverage Report - org.apache.commons.flatfile.DynamicField
 
Classes in this File Line Coverage Branch Coverage Complexity
DynamicField
64%
42/65
30%
6/20
1.441
DynamicField$1
N/A
N/A
1.441
DynamicField$Bounds
90%
30/33
50%
6/12
1.441
DynamicField$Bounds$1
71%
5/7
N/A
1.441
 
 1  93
 /*
 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;
 18  
 
 19  
 import java.io.IOException;
 20  
 import java.io.InputStream;
 21  
 import java.io.ObjectInputStream;
 22  
 import java.io.ObjectOutputStream;
 23  
 import java.io.OutputStream;
 24  
 import java.io.Serializable;
 25  
 import java.util.Arrays;
 26  
 
 27  
 import org.apache.commons.io.IOUtils;
 28  
 import org.apache.commons.io.output.ByteArrayOutputStream;
 29  
 import org.apache.commons.lang3.ArrayUtils;
 30  
 import org.apache.commons.lang3.ObjectUtils;
 31  
 import org.apache.commons.lang3.Validate;
 32  
 import org.apache.commons.lang3.builder.HashCodeBuilder;
 33  
 
 34  
 import org.apache.commons.flatfile.util.ThresholdingInputStream;
 35  
 
 36  
 /**
 37  
  * Dynamically-resizable field. Supports pad/justify, but only relevant when an assigned value is too small for the
 38  
  * field AND underflow == IGNORE.
 39  
  * @version $Revision: 1301248 $ $Date: 2012-03-15 17:20:49 -0500 (Thu, 15 Mar 2012) $
 40  
  */
 41  93
 public class DynamicField extends PadJustifyFieldSupport {
 42  
     /**
 43  
      * Bounds
 44  
      */
 45  138
     public static class Bounds implements Serializable {
 46  
         /**
 47  
          * Immutable range of all valid lengths.
 48  
          */
 49  6
         public static final Bounds ALL_VALID = new Bounds() {
 50  
             private static final long serialVersionUID = -2039931927562380883L;
 51  
 
 52  
             {
 53  6
                 this.minimum = 0;
 54  6
                 this.maximum = Integer.MAX_VALUE;
 55  3
             }
 56  
 
 57  
             /**
 58  
              * {@inheritDoc}
 59  
              */
 60  
             public void setMinimum(int minimum) {
 61  0
             }
 62  
 
 63  
             /**
 64  
              * {@inheritDoc}
 65  
              */
 66  
             public void setMaximum(int maximum) {
 67  0
             }
 68  
         };
 69  
 
 70  
         /**
 71  
          * Default bounds.
 72  
          */
 73  6
         public static final Bounds DEFAULT = ALL_VALID;
 74  
 
 75  3
         private static final long serialVersionUID = 1L;
 76  
 
 77  
         /** Minimum */
 78  
         protected int minimum;
 79  
 
 80  
         /** Maximum */
 81  
         protected int maximum;
 82  
 
 83  
         /**
 84  
          * Create a new Bounds.
 85  
          * @param minimum size
 86  
          * @param maximum size
 87  
          */
 88  312
         public Bounds(int minimum, int maximum) {
 89  312
             setMinimum(minimum);
 90  312
             setMaximum(maximum);
 91  312
         }
 92  
 
 93  
         /**
 94  
          * Create a new Bounds instance.
 95  
          * @param example to copy
 96  
          */
 97  135
         private Bounds(Bounds example) {
 98  270
             this(example.getMinimum(), example.getMaximum());
 99  270
         }
 100  
 
 101  
         /**
 102  
          * Create a new Bounds instance.
 103  
          */
 104  9
         private Bounds() {
 105  6
         }
 106  
 
 107  
         /**
 108  
          * Get the maximum.
 109  
          * @return int
 110  
          */
 111  
         public int getMaximum() {
 112  2106
             return maximum;
 113  
         }
 114  
 
 115  
         /**
 116  
          * Set the maximum.
 117  
          * @param maximum int
 118  
          */
 119  
         public void setMaximum(int maximum) {
 120  540
             Validate.isTrue(maximum <= ALL_VALID.getMaximum(), "maximum value %d > %d", maximum,
 121  180
                 ALL_VALID.getMaximum());
 122  360
             this.maximum = maximum;
 123  360
         }
 124  
 
 125  
         /**
 126  
          * Get the minimum.
 127  
          * @return int
 128  
          */
 129  
         public int getMinimum() {
 130  2004
             return minimum;
 131  
         }
 132  
 
 133  
         /**
 134  
          * Set the minimum.
 135  
          * @param minimum int
 136  
          */
 137  
         public void setMinimum(int minimum) {
 138  531
             Validate.isTrue(minimum >= ALL_VALID.getMinimum(), "minimum value %s < %s", minimum,
 139  177
                 ALL_VALID.getMinimum());
 140  354
             this.minimum = minimum;
 141  354
         }
 142  
 
 143  
         /**
 144  
          * {@inheritDoc}
 145  
          */
 146  
         public boolean equals(Object o) {
 147  54
             if (o == this) {
 148  0
                 return true;
 149  
             }
 150  54
             if (!(o instanceof Bounds)) {
 151  0
                 return false;
 152  
             }
 153  54
             Bounds ob = (Bounds) o;
 154  54
             return ob.getMaximum() == getMaximum() && ob.getMinimum() == getMinimum();
 155  
         }
 156  
 
 157  
         /**
 158  
          * {@inheritDoc}
 159  
          */
 160  
         public int hashCode() {
 161  0
             return new HashCodeBuilder().append(minimum).append(maximum).toHashCode();
 162  
         }
 163  
 
 164  
         /**
 165  
          * Validate a field and value against these Bounds.
 166  
          * @param value to validate
 167  
          * @param df field to validate
 168  
          */
 169  
         public void validate(byte[] value, DynamicField df) {
 170  384
             df.getUnderflow().check(value, getMinimum());
 171  372
             df.getOverflow().check(value, getMaximum());
 172  336
         }
 173  
     }
 174  
 
 175  
     /** Serialization version */
 176  
     private static final long serialVersionUID = -319053179741813727L;
 177  
 
 178  
     private Bounds bounds;
 179  
     private Overflow overflow;
 180  
     private Underflow underflow;
 181  
     private transient ByteArrayOutputStream buffer;
 182  
 
 183  
     /**
 184  
      * Create a new DynamicField.
 185  
      */
 186  
     public DynamicField() {
 187  84
         this(new Bounds(Bounds.DEFAULT));
 188  84
     }
 189  
 
 190  
     /**
 191  
      * Create a new DynamicField.
 192  
      * @param bounds bounds
 193  
      */
 194  84
     public DynamicField(Bounds bounds) {
 195  84
         setBounds(bounds);
 196  84
     }
 197  
 
 198  
     /**
 199  
      * {@inheritDoc}
 200  
      */
 201  
     public synchronized byte[] getValue() {
 202  414
         initialize(ArrayUtils.EMPTY_BYTE_ARRAY);
 203  414
         return buffer.toByteArray();
 204  
     }
 205  
 
 206  
     /**
 207  
      * {@inheritDoc}
 208  
      */
 209  
     public int length() {
 210  288
         initialize(ArrayUtils.EMPTY_BYTE_ARRAY);
 211  288
         return buffer.size();
 212  
     }
 213  
 
 214  
     /**
 215  
      * Read up to <code>maximumLength</code> bytes from the specified InputStream or stop at EOF. This will rarely be
 216  
      * what you want. Instead, consider using InputFilteringDynamicField. In the case that &lt;
 217  
      * <code>minimumLength</code> bytes are available from <code>is</code> the <code>justify</code> and
 218  
      * <code>pad</code> options come into play.
 219  
      * @param is the InputStream from which to read data.
 220  
      * @throws IOException on problems with I/O, duh...
 221  
      */
 222  
     public synchronized void readFrom(InputStream is) throws IOException {
 223  468
         initialize();
 224  468
         buffer.reset();
 225  468
         IOUtils.copy(new ThresholdingInputStream(is, getBounds().getMaximum()), buffer);
 226  468
     }
 227  
 
 228  
     /**
 229  
      * {@inheritDoc}
 230  
      */
 231  
     public synchronized void setValue(byte[] b) {
 232  384
         getBounds().validate(b, this);
 233  336
         initialize();
 234  336
         iSetValue(b);
 235  336
     }
 236  
 
 237  
     /**
 238  
      * {@inheritDoc}
 239  
      */
 240  
     public synchronized void writeTo(OutputStream os) throws IOException {
 241  174
         initialize(ArrayUtils.EMPTY_BYTE_ARRAY);
 242  174
         buffer.writeTo(os);
 243  174
     }
 244  
 
 245  
     /**
 246  
      * Get the overflow.
 247  
      * @return Overflow
 248  
      */
 249  
     public Overflow getOverflow() {
 250  468
         return overflow == null ? Overflow.ERROR : overflow;
 251  
     }
 252  
 
 253  
     /**
 254  
      * Set the overflow.
 255  
      * @param overflow Overflow
 256  
      */
 257  
     public void setOverflow(Overflow overflow) {
 258  0
         this.overflow = overflow;
 259  0
     }
 260  
 
 261  
     /**
 262  
      * Get the underflow.
 263  
      * @return Underflow
 264  
      */
 265  
     public Underflow getUnderflow() {
 266  492
         return underflow == null ? Underflow.ERROR : underflow;
 267  
     }
 268  
 
 269  
     /**
 270  
      * Set the underflow.
 271  
      * @param underflow Underflow
 272  
      */
 273  
     public void setUnderflow(Underflow underflow) {
 274  0
         this.underflow = underflow;
 275  0
     }
 276  
 
 277  
     /**
 278  
      * {@inheritDoc}
 279  
      */
 280  
     public int hashCode() {
 281  0
         return new HashCodeBuilder().append(getBounds()).append(getValue()).toHashCode();
 282  
     }
 283  
 
 284  
     /**
 285  
      * {@inheritDoc}
 286  
      */
 287  
     public boolean equals(Object other) {
 288  0
         if (other == this) {
 289  0
             return true;
 290  
         }
 291  0
         if (!(other instanceof DynamicField)) {
 292  0
             return false;
 293  
         }
 294  0
         DynamicField odf = (DynamicField) other;
 295  0
         return ObjectUtils.equals(odf.getBounds(), getBounds())
 296  0
                 && Arrays.equals(odf.getValue(), getValue());
 297  
     }
 298  
 
 299  
     /**
 300  
      * {@inheritDoc}
 301  
      */
 302  
     protected int getPadJustifyLength() {
 303  414
         return getBounds().getMinimum();
 304  
     }
 305  
 
 306  
     /**
 307  
      * Protected inner setValue
 308  
      * @param value byte[]
 309  
      */
 310  
     protected synchronized void iSetValue(byte[] value) {
 311  414
         super.setValue(value);
 312  414
     }
 313  
 
 314  
     /**
 315  
      * {@inheritDoc}
 316  
      */
 317  
     public DynamicField clone() {
 318  186
         DynamicField result = (DynamicField) super.clone();
 319  186
         result.setBounds(new Bounds(getBounds()));
 320  186
         result.buffer = null;
 321  186
         result.setValue(getValue());
 322  186
         return result;
 323  
     }
 324  
 
 325  
     /**
 326  
      * Get the bounds.
 327  
      * @return Bounds
 328  
      */
 329  
     public Bounds getBounds() {
 330  1866
         return bounds;
 331  
     }
 332  
 
 333  
     /**
 334  
      * Set the bounds.
 335  
      * @param bounds Bounds
 336  
      */
 337  
     public void setBounds(Bounds bounds) {
 338  270
         this.bounds = Validate.notNull(bounds, "bounds cannot be null");
 339  270
     }
 340  
 
 341  
     /**
 342  
      * Serialization
 343  
      * @param out to write to
 344  
      * @throws IOException on error
 345  
      */
 346  
     private synchronized void writeObject(ObjectOutputStream out) throws IOException {
 347  0
         out.writeObject(getBounds());
 348  0
         initialize(ArrayUtils.EMPTY_BYTE_ARRAY);
 349  0
         out.write(buffer.size());
 350  0
         buffer.writeTo(out);
 351  0
     }
 352  
 
 353  
     /**
 354  
      * Serialization
 355  
      * @param in to read from
 356  
      * @throws IOException on error
 357  
      * @throws ClassNotFoundException if Object class not found
 358  
      */
 359  
     private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
 360  0
         setBounds((Bounds) in.readObject());
 361  0
         int n = in.read();
 362  0
         initialize();
 363  0
         if (n > 0) {
 364  0
             IOUtils.copy(new ThresholdingInputStream(in, n), buffer);
 365  
         }
 366  0
     }
 367  
 
 368  
     /**
 369  
      * Intitialize this {@link DynamicField}.
 370  
      */
 371  
     private void initialize() {
 372  804
         initialize(null);
 373  804
     }
 374  
 
 375  
     /**
 376  
      * Initialize this {@link DynamicField} from a given byte[].
 377  
      * @param value byte[]
 378  
      */
 379  
     private synchronized void initialize(byte[] value) {
 380  1680
         if (buffer == null) {
 381  270
             buffer = new ByteArrayOutputStream();
 382  270
             if (value != null) {
 383  78
                 iSetValue(value);
 384  
             }
 385  
         }
 386  1680
     }
 387  
 
 388  
 }