Coverage Report - ca.uhn.hl7v2.preparser.DatumPath
 
Classes in this File Line Coverage Branch Coverage Complexity
DatumPath
80%
72/90
75%
47/62
3.235
 
 1  
 
 2  
 package ca.uhn.hl7v2.preparser;
 3  
 
 4  
 
 5  
 import java.util.ArrayList;
 6  
 
 7  
 /** An object of this class represents a variable-size path for identifying
 8  
 the location of a datum within an HL7 message, which we can use for
 9  
 maintaining parser state and for generating a suitable string key (in the
 10  
 ZYX[a]-b[c]-d-e style) for a piece of data in the message (see toString()).
 11  
 
 12  
 The elements are: 
 13  
 segmentID / segmentRepIdx / fieldIdx / fieldRepIdx / compIdx / subcompIdx 
 14  
 
 15  
 ("rep" means "repetition")
 16  
 
 17  
 segmentID is a String, the rest are Integers.
 18  
 
 19  
 It is variable-size path-style in that if it has a size of 1, the one element
 20  
 will be the segmentID; if it has a size of two, element 0 will be the segmentID
 21  
 and element 1 will be the segmentRepIdx, etc.  This class can't represent a
 22  
 fieldIdx without having segmentID / segmentRepIdx, etc. etc. 
 23  
 
 24  
 possible sizes: 0 to 6 inclusive
 25  
 
 26  
 As toString() simply converts this's integer values to strings (1 => "1"), and
 27  
 since for some reason the ZYX[a]-b[c]-d-e style counts b, d, e starting from 1
 28  
 and a, c from 0 -- it is intended that one store the numeric values in this
 29  
 class starting from 1 for fieldIdx (element 2), compIdx (4) and subcompIdx
 30  
 (5), and from 0 for segmentRepIdx (1) and fieldRepIdx (3).  default values
 31  
 provided by setSize() and by toString() do this.
 32  
 */
 33  
 public class DatumPath implements Cloneable {
 34  
 
 35  
         public static final int s_maxSize = 6;
 36  
 
 37  10020
         protected ArrayList<Object> m_path = null;
 38  
 
 39  
         public DatumPath()
 40  10020
         {
 41  10020
                 m_path = new ArrayList<Object>(s_maxSize);
 42  10020
         }
 43  
 
 44  
         /** copy constructor */
 45  
         public DatumPath(DatumPath other)
 46  
         {
 47  3610
                 this();
 48  3610
                 copy(other);
 49  3610
         }
 50  
 
 51  
         public boolean equals(Object otherObject)
 52  
         {
 53  0
         if (otherObject == null) return false;
 54  0
         if (!getClass().equals(otherObject.getClass())) return false;
 55  0
         DatumPath other = (DatumPath)otherObject;
 56  0
                 return m_path.equals(other.m_path);
 57  
         }
 58  
 
 59  
         /** Works like String.startsWith: 
 60  
         returns true iff prefix.size() <= this.size()
 61  
          AND if, for 0 <= i < prefix.size(), this.get(i).equals(prefix.get(i))
 62  
         */
 63  
         public boolean startsWith(DatumPath prefix)
 64  
         {
 65  10130
                 boolean ret = false;
 66  10130
                 if(prefix.size() <= this.size()) {
 67  8105
                         ret = true;
 68  49310
                         for(int i=0; i<prefix.size(); ++i)
 69  41205
                                 ret &= this.get(i).equals(prefix.get(i));
 70  
                 }
 71  10130
                 return ret;
 72  
         }
 73  
 
 74  
         /** like a copy constructor without the constructor */
 75  
         public void copy(DatumPath other)
 76  
         {
 77  4310
                 setSize(0);
 78  25370
                 for(int i=0; i<other.size(); ++i)
 79  21060
                         add(other.get(i));
 80  4310
         }
 81  
 
 82  
         /** set() sets an element of the path.  
 83  
         
 84  
         idx must be in [0, size()). else => IndexOutOfBoundsException. 
 85  
         
 86  
         (new_value == null) => NullPointerException  
 87  
 
 88  
         new_value must be either a String or an Integer depending on what part 
 89  
         of the path you're setting:  
 90  
 
 91  
         (idx == 0) => String
 92  
         (idx >= 1) => Integer
 93  
 
 94  
         If new_value can't be cast to the appropriate type, a ClassCastException 
 95  
         is thrown before new_value is stored.
 96  
 
 97  
         Of course, on success, this will discard it's reference that used to be at
 98  
         position idx.
 99  
         */
 100  
         public void set(int idx, Object new_value)
 101  
         {
 102  3815
                 if((0 <= idx) && (idx < m_path.size())) {
 103  3815
                         if(new_value != null) {
 104  3815
                                 if(idx == 0)
 105  0
                                         m_path.set(idx, new_value);
 106  3815
                                 else if(idx >= 1)
 107  3815
                                         m_path.set(idx, new_value);
 108  
                         }
 109  
                         else
 110  0
                                 throw new NullPointerException();
 111  
                 }
 112  
                 else
 113  0
                         throw new IndexOutOfBoundsException();
 114  3815
         }
 115  
 
 116  
         /** get() returns an element, which will be either a String or an Integer.
 117  
 
 118  
         ((idx == 0) => String
 119  
         (idx >= 1) => Integer
 120  
         ((idx < 0) || (idx >= size())) => IndexOutOfBoundsException
 121  
 
 122  
         We will attempt to cast the gotten object to the appropriate type before
 123  
         returning it as an Object.  That way, if there's an object of the wrong type
 124  
         in the wrong place in here (that got past set() somehow), then a
 125  
         ClassCastException will be thrown even if the caller of this function
 126  
         doesn't try to cast it.  (consider System.out.println("val: " + path.get(n))
 127  
         nothing would barf it this get() wasn't vigilant.)
 128  
         */
 129  
         public Object get(int idx)
 130  
         {
 131  123445
                 Object gottenObj = m_path.get(idx);
 132  123445
                 if(idx == 0)
 133  23425
                         return gottenObj;
 134  
                 else
 135  100020
                         return gottenObj;
 136  
         }
 137  
 
 138  137360
         public int size() { return m_path.size(); }
 139  
 
 140  
         /** toString() outputs the path (from segmentID onward) in the ZYX[a]-b[c]-d-e
 141  
         style (TODO: give it a name), suitable for a key in a map of 
 142  
         message datum paths to values. 
 143  
         
 144  
         Integer values are converted to strings directly (1 => "1") so when you
 145  
         constructed this you should have started counting from 1 for everything but
 146  
         the "repeat" fields, if you truly want the ZYX[a]-b[c]-d-e style.
 147  
 
 148  
         If toString() is called when this has a size in [1, 6) (=> missing numeric
 149  
         elments), then we act as though the elements in [size(), 6) are 0 or 1 as
 150  
         appropriate for each element.  We don't provide a default for the element 0
 151  
         (the String element): will throw an IndexOutOfBoundsException if (size() ==
 152  
         1).
 153  
 
 154  
         eg. a (new DatumPath()).add(new String("ZYX")).add(2).add(6).toString() 
 155  
         would yield "ZYX[2]-6[0]-1-1"
 156  
         */
 157  
         public String toString()
 158  
         {
 159  
 
 160  1730
                 StringBuilder strbuf = new StringBuilder();
 161  
 
 162  1730
                 if(m_path.size() >= 1) {
 163  1730
                         DatumPath extendedCopy = (DatumPath)this.clone();
 164  1730
                         extendedCopy.setSize(s_maxSize);
 165  
 
 166  12110
                         for(int i=0; i<extendedCopy.size(); ++i) {
 167  10380
                                 if(i == 0)
 168  1730
                                         strbuf.append(extendedCopy.get(0));
 169  8650
                                 else if((i == 1) || (i == 3))
 170  3460
                                         strbuf.append("[").append(extendedCopy.get(i)).append("]");
 171  5190
                                 else if((i == 2) || (i == 4) || (i == 5))
 172  5190
                                         strbuf.append("-").append(extendedCopy.get(i));
 173  
                         }
 174  1730
                 }
 175  
                 else 
 176  0
                         throw new IndexOutOfBoundsException();        
 177  
 
 178  1730
                 return strbuf.toString();
 179  
         }
 180  
 
 181  
         /** add() grows this by 1, inserting newValue at the end.
 182  
         newValue must be a String or an Integer depending on the index where it will
 183  
         be inserted, as noted at DatumPath.set().  
 184  
         returns this.
 185  
         (newValue == null) => NullPointerException 
 186  
         */
 187  
         public DatumPath add(Object newValue)
 188  
         {
 189  
 //                m_path.ensureCapacity(m_path.size() + 1);
 190  
 //                set(m_path.size() - 1, newValue);
 191  56485
                 m_path.add(newValue);
 192  56485
                 return this;
 193  
         }
 194  
 
 195  
         /** Like add(String).  convenient wrapper for add(Object), when the object
 196  
         to be added must be an Integer anyway (size() > 0 on entry).  
 197  
 
 198  
         For the user, it turns 
 199  
         path.add(new Integer(i)).add(new Integer(j)).add(new Integer(k)) 
 200  
         into 
 201  
         path.add(i).add(j).add(k), that's all.  
 202  
 
 203  
         size() == 0 on entry throws a ClassCastException (which it is, kindof), 
 204  
         otherwise calls add(new Integer(new_value)).
 205  
         */
 206  
         public DatumPath add(int new_value)
 207  
         {
 208  9125
                 if(size() > 0)
 209  9125
                         add(new Integer(new_value));
 210  
                 else 
 211  0
                         throw new ClassCastException();
 212  
 
 213  9125
                 return this;
 214  
         }
 215  
 
 216  
         /** convenience!  Like add(int), but the other way around. */
 217  
         public DatumPath add(String new_value)
 218  
         {
 219  6460
                 if(size() == 0) 
 220  6460
                         add((Object)new_value);
 221  
                 else
 222  0
                         throw new ClassCastException();
 223  
 
 224  6460
                 return this;
 225  
         }
 226  
 
 227  
         /** setSize(): resize.  If this will grow the object, then we put default
 228  
         values into the new elements: "" into the String element, Integer(1) into the
 229  
         elements 2, 4, and 5, and Integer(0) into elements 1 and 3.
 230  
         returns this.
 231  
         */
 232  
         public DatumPath setSize(int newSize)
 233  
         {
 234  9085
                 int oldSize = m_path.size();
 235  
                 
 236  12900
                 while (m_path.size() < newSize) {
 237  3815
                         m_path.add(null);
 238  
                 }
 239  
                 
 240  14145
                 while (m_path.size() > newSize) {
 241  5060
                         m_path.remove(m_path.size() - 1);
 242  
                 }
 243  
 
 244  9085
                 if(newSize > oldSize) {
 245  
                         // give the new elements some default values: 
 246  6320
                         for(int i=oldSize; i<newSize; ++i) {
 247  3815
                                 if(i == 0)
 248  0
                                         set(i, "");
 249  
                                 else
 250  3815
                                         set(i, (i==1 || i==3) ? 0 : 1);
 251  
                         }
 252  
                 }
 253  
 
 254  9085
                 return this;
 255  
         }
 256  
 
 257  
         /** setSize(0).  returns this. */
 258  
         public DatumPath clear() 
 259  
         {
 260  140
                 setSize(0);
 261  140
                 return this;
 262  
         }
 263  
 
 264  
         public Object clone()
 265  
         {
 266  1730
                 return new DatumPath(this);
 267  
         }
 268  
 
 269  
         /* Compare the numeric parts of "this" and "other".  string-style, start from
 270  
         the left: if this[1] < other[1], then return true, if this[1] > other[1] then
 271  
         return false, else repeat with [2] ... if we compare all elements, then return
 272  
         false (they're the same.)
 273  
 
 274  
         What are actually compared are copies of this and other that have been grown
 275  
         to s_maxSize (default values in effect), so they'll have the same size.
 276  
         
 277  
         This is just a little thing that gets used in the class XML.  Look there for 
 278  
         a justification of it's existence.
 279  
 
 280  
         ex. [1, 1, 1, 1] < [1, 1, 1, 2] 
 281  
         [1, 2, 1, 1] < [1, 2, 1, 2]
 282  
         [1, 1, 5, 5] < [1, 2]
 283  
         [1, 1] < [1, 1, 5, 5] 
 284  
         */
 285  
         public boolean numbersLessThan(DatumPath other)
 286  
         {
 287  940
                 DatumPath extendedCopyThis = new DatumPath(this);
 288  940
                 extendedCopyThis.setSize(s_maxSize);
 289  
 
 290  940
                 DatumPath extendedCopyOther = new DatumPath(other);
 291  940
                 extendedCopyOther.setSize(s_maxSize);
 292  
 
 293  940
                 boolean lessThan = false;
 294  4145
                 for(int i=1; !lessThan && (i<s_maxSize); ++i) {
 295  3205
                         int this_i = ((Integer)extendedCopyThis.get(i));
 296  3205
                         int other_i = ((Integer)extendedCopyOther.get(i));
 297  3205
                         lessThan |= (this_i < other_i);
 298  
                 }
 299  
 
 300  940
                 return lessThan;
 301  
         }
 302  
 
 303  
         public static void main(String args[])
 304  
         {
 305  0
                 DatumPath dp = new DatumPath();
 306  0
                 dp.add("ZYX");
 307  0
                 dp.add(new Integer(42));
 308  
 
 309  0
                 DatumPath dp2 = new DatumPath().add(-42);
 310  
 
 311  0
                 System.out.println(dp);
 312  0
                 System.out.println(dp2);
 313  0
         }
 314  
 }
 315