Coverage Report - org.apache.commons.flatfile.DynamicField
 
Classes in this File Line Coverage Branch Coverage Complexity
DynamicField
66%
42/64
33%
6/18
1.324
DynamicField$1
N/A
N/A
1.324
DynamicField$Bounds
90%
26/29
50%
6/12
1.324
DynamicField$Bounds$1
67%
4/6
N/A
1.324
 
 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;
 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.lang.ArrayUtils;
 30  
 import org.apache.commons.lang.ObjectUtils;
 31  
 import org.apache.commons.lang.Validate;
 32  
 import org.apache.commons.lang.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: 758023 $ $Date: 2009-03-24 16:09:19 -0500 (Tue, 24 Mar 2009) $
 40  
  */
 41  31
 public class DynamicField extends PadJustifyFieldSupport {
 42  
     /**
 43  
      * Bounds
 44  
      */
 45  46
     public static class Bounds implements Serializable {
 46  
         /**
 47  
          * Immutable range of all valid lengths.
 48  
          */
 49  1
         public static final Bounds ALL_VALID = new Bounds() {
 50  
             private static final long serialVersionUID = -2039931927562380883L;
 51  
 
 52  
             {
 53  1
                 this.minimum = 0;
 54  1
                 this.maximum = Integer.MAX_VALUE;
 55  1
             }
 56  
 
 57  
             /**
 58  
              * {@inheritDoc}
 59  
              */
 60  
             public void setMinimum(int minimum) {
 61  0
             }
 62  
 
 63  
             /**
 64  
              * {@inheritDoc}
 65  
              */
 66  1
             public void setMaximum(int maximum) {
 67  0
             }
 68  
         };
 69  
 
 70  
         /**
 71  
          * Default bounds.
 72  
          */
 73  1
         public static final Bounds DEFAULT = ALL_VALID;
 74  
 
 75  
         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  52
         public Bounds(int minimum, int maximum) {
 89  52
             setMinimum(minimum);
 90  52
             setMaximum(maximum);
 91  52
         }
 92  
 
 93  
         /**
 94  
          * Create a new Bounds instance.
 95  
          * @param example to copy
 96  
          */
 97  
         private Bounds(Bounds example) {
 98  45
             this(example.getMinimum(), example.getMaximum());
 99  45
         }
 100  
 
 101  
         /**
 102  
          * Create a new Bounds instance.
 103  
          */
 104  1
         private Bounds() {
 105  1
         }
 106  
 
 107  
         /**
 108  
          * Get the maximum.
 109  
          * @return int
 110  
          */
 111  
         public int getMaximum() {
 112  351
             return maximum;
 113  
         }
 114  
 
 115  
         /**
 116  
          * Set the maximum.
 117  
          * @param maximum int
 118  
          */
 119  
         public void setMaximum(int maximum) {
 120  60
             Validate.isTrue(maximum <= ALL_VALID.getMaximum(), "maximum value > "
 121  
                     + ALL_VALID.getMaximum(), maximum);
 122  60
             this.maximum = maximum;
 123  60
         }
 124  
 
 125  
         /**
 126  
          * Get the minimum.
 127  
          * @return int
 128  
          */
 129  
         public int getMinimum() {
 130  334
             return minimum;
 131  
         }
 132  
 
 133  
         /**
 134  
          * Set the minimum.
 135  
          * @param minimum int
 136  
          */
 137  
         public void setMinimum(int minimum) {
 138  59
             Validate.isTrue(minimum >= ALL_VALID.getMinimum(), "minimum value < "
 139  
                     + ALL_VALID.getMinimum(), minimum);
 140  59
             this.minimum = minimum;
 141  59
         }
 142  
 
 143  
         /**
 144  
          * {@inheritDoc}
 145  
          */
 146  
         public boolean equals(Object o) {
 147  9
             if (o == this) {
 148  0
                 return true;
 149  
             }
 150  9
             if (!(o instanceof Bounds)) {
 151  0
                 return false;
 152  
             }
 153  9
             Bounds ob = (Bounds) o;
 154  9
             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  64
             df.getUnderflow().check(value, getMinimum());
 171  62
             df.getOverflow().check(value, getMaximum());
 172  56
         }
 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  14
         this(new Bounds(Bounds.DEFAULT));
 188  14
     }
 189  
 
 190  
     /**
 191  
      * Create a new DynamicField.
 192  
      * @param bounds bounds
 193  
      */
 194  14
     public DynamicField(Bounds bounds) {
 195  14
         setBounds(bounds);
 196  14
     }
 197  
 
 198  
     /**
 199  
      * {@inheritDoc}
 200  
      */
 201  
     public synchronized byte[] getValue() {
 202  69
         initialize(ArrayUtils.EMPTY_BYTE_ARRAY);
 203  69
         return buffer.toByteArray();
 204  
     }
 205  
 
 206  
     /**
 207  
      * {@inheritDoc}
 208  
      */
 209  
     public int length() {
 210  48
         initialize(ArrayUtils.EMPTY_BYTE_ARRAY);
 211  48
         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  78
         initialize();
 224  78
         buffer.reset();
 225  78
         IOUtils.copy(new ThresholdingInputStream(is, getBounds().getMaximum()), buffer);
 226  78
     }
 227  
 
 228  
     /**
 229  
      * {@inheritDoc}
 230  
      */
 231  
     public synchronized void setValue(byte[] b) {
 232  64
         getBounds().validate(b, this);
 233  56
         initialize();
 234  56
         iSetValue(b);
 235  56
     }
 236  
 
 237  
     /**
 238  
      * {@inheritDoc}
 239  
      */
 240  
     public synchronized void writeTo(OutputStream os) throws IOException {
 241  29
         initialize(ArrayUtils.EMPTY_BYTE_ARRAY);
 242  29
         buffer.writeTo(os);
 243  29
     }
 244  
 
 245  
     /**
 246  
      * Get the overflow.
 247  
      * @return Overflow
 248  
      */
 249  
     public Overflow getOverflow() {
 250  78
         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  82
         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  
                 && Arrays.equals(odf.getValue(), getValue());
 297  
     }
 298  
 
 299  
     /**
 300  
      * {@inheritDoc}
 301  
      */
 302  
     protected int getPadJustifyLength() {
 303  69
         return getBounds().getMinimum();
 304  
     }
 305  
 
 306  
     /**
 307  
      * Protected inner setValue
 308  
      * @param value byte[]
 309  
      */
 310  
     protected synchronized void iSetValue(byte[] value) {
 311  69
         super.setValue(value);
 312  69
     }
 313  
 
 314  
     /**
 315  
      * {@inheritDoc}
 316  
      */
 317  
     public DynamicField clone() {
 318  31
         DynamicField result = (DynamicField) super.clone();
 319  31
         result.setBounds(new Bounds(getBounds()));
 320  31
         result.buffer = null;
 321  31
         result.setValue(getValue());
 322  31
         return result;
 323  
     }
 324  
 
 325  
     /**
 326  
      * Get the bounds.
 327  
      * @return Bounds
 328  
      */
 329  
     public Bounds getBounds() {
 330  311
         return bounds;
 331  
     }
 332  
 
 333  
     /**
 334  
      * Set the bounds.
 335  
      * @param bounds Bounds
 336  
      */
 337  
     public void setBounds(Bounds bounds) {
 338  45
         Validate.notNull(bounds, "bounds cannot be null");
 339  45
         this.bounds = bounds;
 340  45
     }
 341  
 
 342  
     /**
 343  
      * Serialization
 344  
      * @param out to write to
 345  
      * @throws IOException on error
 346  
      */
 347  
     private synchronized void writeObject(ObjectOutputStream out) throws IOException {
 348  0
         out.writeObject(getBounds());
 349  0
         initialize(ArrayUtils.EMPTY_BYTE_ARRAY);
 350  0
         out.write(buffer.size());
 351  0
         buffer.writeTo(out);
 352  0
     }
 353  
 
 354  
     /**
 355  
      * Serialization
 356  
      * @param in to read from
 357  
      * @throws IOException on error
 358  
      * @throws ClassNotFoundException if Object class not found
 359  
      */
 360  
     private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
 361  0
         setBounds((Bounds) in.readObject());
 362  0
         int n = in.read();
 363  0
         initialize();
 364  0
         if (n > 0) {
 365  0
             IOUtils.copy(new ThresholdingInputStream(in, n), buffer);
 366  
         }
 367  0
     }
 368  
 
 369  
     /**
 370  
      * Intitialize this {@link DynamicField}.
 371  
      */
 372  
     private void initialize() {
 373  134
         initialize(null);
 374  134
     }
 375  
 
 376  
     /**
 377  
      * Initialize this {@link DynamicField} from a given byte[].
 378  
      * @param value byte[]
 379  
      */
 380  
     private synchronized void initialize(byte[] value) {
 381  280
         if (buffer == null) {
 382  45
             buffer = new ByteArrayOutputStream();
 383  45
             if (value != null) {
 384  13
                 iSetValue(value);
 385  
             }
 386  
         }
 387  280
     }
 388  
 
 389  
 }