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  1940
         protected ArrayList<Object> m_path = null;
 38  
 
 39  
         public DatumPath()
 40  1940
         {
 41  1940
                 m_path = new ArrayList<Object>(s_maxSize);
 42  1940
         }
 43  
 
 44  
         /** copy constructor */
 45  
         public DatumPath(DatumPath other)
 46  
         {
 47  1059
                 this();
 48  1059
                 copy(other);
 49  1059
         }
 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  1729
                 boolean ret = false;
 66  1729
                 if(prefix.size() <= this.size()) {
 67  1384
                         ret = true;
 68  7665
                         for(int i=0; i<prefix.size(); ++i)
 69  6281
                                 ret &= this.get(i).equals(prefix.get(i));
 70  
                 }
 71  1729
                 return ret;
 72  
         }
 73  
 
 74  
         /** like a copy constructor without the constructor */
 75  
         public void copy(DatumPath other)
 76  
         {
 77  1307
                 setSize(0);
 78  6696
                 for(int i=0; i<other.size(); ++i)
 79  5389
                         add(other.get(i));
 80  1307
         }
 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  1824
                 if((0 <= idx) && (idx < m_path.size())) {
 103  1824
                         if(new_value != null) {
 104  1824
                                 if(idx == 0)
 105  0
                                         m_path.set(idx, new_value);
 106  1824
                                 else if(idx >= 1)
 107  1824
                                         m_path.set(idx, new_value);
 108  
                         }
 109  
                         else
 110  0
                                 throw new NullPointerException();
 111  
                 }
 112  
                 else
 113  0
                         throw new IndexOutOfBoundsException();
 114  1824
         }
 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  24288
                 Object gottenObj = m_path.get(idx);
 132  24288
                 if(idx == 0)
 133  4993
                         return gottenObj;
 134  
                 else
 135  19295
                         return gottenObj;
 136  
         }
 137  
 
 138  28938
         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  683
                 StringBuilder strbuf = new StringBuilder();
 161  
 
 162  683
                 if(m_path.size() >= 1) {
 163  683
                         DatumPath extendedCopy = (DatumPath)this.clone();
 164  683
                         extendedCopy.setSize(s_maxSize);
 165  
 
 166  4781
                         for(int i=0; i<extendedCopy.size(); ++i) {
 167  4098
                                 if(i == 0)
 168  683
                                         strbuf.append(extendedCopy.get(0));
 169  3415
                                 else if((i == 1) || (i == 3))
 170  1366
                                         strbuf.append("[").append(extendedCopy.get(i)).append("]");
 171  2049
                                 else if((i == 2) || (i == 4) || (i == 5))
 172  2049
                                         strbuf.append("-").append(extendedCopy.get(i));
 173  
                         }
 174  683
                 }
 175  
                 else 
 176  0
                         throw new IndexOutOfBoundsException();        
 177  
 
 178  683
                 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  10508
                 m_path.add(newValue);
 192  10508
                 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  1374
                 if(size() > 0)
 209  1374
                         add(new Integer(new_value));
 210  
                 else 
 211  0
                         throw new ClassCastException();
 212  
 
 213  1374
                 return this;
 214  
         }
 215  
 
 216  
         /** convenience!  Like add(int), but the other way around. */
 217  
         public DatumPath add(String new_value)
 218  
         {
 219  999
                 if(size() == 0) 
 220  999
                         add((Object)new_value);
 221  
                 else
 222  0
                         throw new ClassCastException();
 223  
 
 224  999
                 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  2707
                 int oldSize = m_path.size();
 235  
                 
 236  4531
                 while (m_path.size() < newSize) {
 237  1824
                         m_path.add(null);
 238  
                 }
 239  
                 
 240  4151
                 while (m_path.size() > newSize) {
 241  1444
                         m_path.remove(m_path.size() - 1);
 242  
                 }
 243  
 
 244  2707
                 if(newSize > oldSize) {
 245  
                         // give the new elements some default values: 
 246  2678
                         for(int i=oldSize; i<newSize; ++i) {
 247  1824
                                 if(i == 0)
 248  0
                                         set(i, "");
 249  
                                 else
 250  1824
                                         set(i, (i==1 || i==3) ? 0 : 1);
 251  
                         }
 252  
                 }
 253  
 
 254  2707
                 return this;
 255  
         }
 256  
 
 257  
         /** setSize(0).  returns this. */
 258  
         public DatumPath clear() 
 259  
         {
 260  28
                 setSize(0);
 261  28
                 return this;
 262  
         }
 263  
 
 264  
         public Object clone()
 265  
         {
 266  683
                 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  188
                 DatumPath extendedCopyThis = new DatumPath(this);
 288  188
                 extendedCopyThis.setSize(s_maxSize);
 289  
 
 290  188
                 DatumPath extendedCopyOther = new DatumPath(other);
 291  188
                 extendedCopyOther.setSize(s_maxSize);
 292  
 
 293  188
                 boolean lessThan = false;
 294  829
                 for(int i=1; !lessThan && (i<s_maxSize); ++i) {
 295  641
                         int this_i = ((Integer)extendedCopyThis.get(i));
 296  641
                         int other_i = ((Integer)extendedCopyOther.get(i));
 297  641
                         lessThan |= (this_i < other_i);
 298  
                 }
 299  
 
 300  188
                 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