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 |