| Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
| DatumPath |
|
| 3.235294117647059;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 |