1 /**
2 * The contents of this file are subject to the Mozilla Public License Version 1.1
3 * (the "License"); you may not use this file except in compliance with the License.
4 * You may obtain a copy of the License at http://www.mozilla.org/MPL/
5 * Software distributed under the License is distributed on an "AS IS" basis,
6 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the
7 * specific language governing rights and limitations under the License.
8 *
9 * The Original Code is "SegmentFinder.java". Description:
10 * "A tool for getting segments by name within a message or part of a message."
11 *
12 * The Initial Developer of the Original Code is University Health Network. Copyright (C)
13 * 2002. All Rights Reserved.
14 *
15 * Contributor(s): ______________________________________.
16 *
17 * Alternatively, the contents of this file may be used under the terms of the
18 * GNU General Public License (the "GPL"), in which case the provisions of the GPL are
19 * applicable instead of those above. If you wish to allow use of your version of this
20 * file only under the terms of the GPL and not to allow others to use your version
21 * of this file under the MPL, indicate your decision by deleting the provisions above
22 * and replace them with the notice and other provisions required by the GPL License.
23 * If you do not delete the provisions above, a recipient may use your version of
24 * this file under either the MPL or the GPL.
25 *
26 */
27
28 package ca.uhn.hl7v2.util;
29
30 import ca.uhn.hl7v2.model.*;
31 import ca.uhn.hl7v2.HL7Exception;
32 import java.util.regex.*;
33
34 /**
35 * A tool for getting segments by name within a message or part of a message.
36 * @author Bryan Tripp
37 */
38 public class SegmentFinder extends MessageNavigator {
39
40 private static final Pattern VALID_PATTERN_PATTERN = Pattern.compile("[\\w\\*\\?]*");
41 private static final Pattern LITERAL_UNBOUNDED = Pattern.compile("\\*");
42 private static final Pattern LITERAL_OPTIONAL = Pattern.compile("\\?");
43
44 /**
45 * Creates a new instance of SegmentFinder.
46 * @param root the scope of searches -- may be a whole message or only a branch
47 */
48 public SegmentFinder(Group root) {
49 super(root);
50 }
51
52 /**
53 * Returns the first segment with a name that matches the given pattern, in a depth-first search.
54 * Repeated searches are initiated from the location just AFTER where the last segment was found.
55 * Call reset() is this is not desired. Note: this means that the current location will not be found.
56 * @param namePattern the name of the segment to find. The wildcard * means any number
57 * of arbitrary characters; the wildcard ? one arbitrary character
58 * (eg "P*" or "*ID" or "???" or "P??" would match on PID).
59 * @param rep the repetition of the segment to return
60 */
61 public Segment findSegment(String namePattern, int rep) throws HL7Exception {
62 Structure s;
63 do {
64 s = findStructure(namePattern, rep);
65 } while (!Segment.class.isAssignableFrom(s.getClass()));
66 return (Segment) s;
67 }
68
69 /**
70 * As findSegment(), but will only return a group.
71 */
72 public Group findGroup(String namePattern, int rep) throws HL7Exception {
73 Structure s;
74 do {
75 s = findStructure(namePattern, rep);
76 } while (!Group.class.isAssignableFrom(s.getClass()));
77 return (Group) s;
78 }
79
80 /**
81 * Returns the first matching structure AFTER the current position
82 */
83 private Structure findStructure(String namePattern, int rep) throws HL7Exception {
84 Structure s = null;
85
86 while (s == null) {
87 String currentNameInParent = iterate(false, false);
88 String currentName = getCurrentStructure(0).getName();
89 if (matches(namePattern, currentName) || matches(namePattern, currentNameInParent)) {
90 s = getCurrentStructure(rep);
91 }
92 }
93 return s;
94 }
95
96 /**
97 * Returns the first segment with a name matching the given pattern that is a sibling of
98 * the structure at the current location. Other parts of the message are
99 * not searched (in contrast to findSegment).
100 * As a special case, if the pointer is at the root, the children of the root
101 * are searched.
102 * @param namePattern the name of the segment to get. The wildcad * means any number
103 * of arbitrary characters; the wildard ? one arbitrary character
104 * (eg "P*" or "*ID" or "???" or "P??" would match on PID).
105 * @param rep the repetition of the segment to return
106 */
107 public Segment getSegment(String namePattern, int rep) throws HL7Exception {
108 Structure s = getStructure(namePattern, rep);
109 if (!Segment.class.isAssignableFrom(s.getClass())) {
110 throw new HL7Exception(s.getName() + " is not a segment");
111 }
112 return (Segment) s;
113 }
114
115 /**
116 * As getSegment() but will only return a group.
117 */
118 public Group getGroup(String namePattern, int rep) throws HL7Exception {
119 Structure s = getStructure(namePattern, rep);
120 if (!Group.class.isAssignableFrom(s.getClass())) {
121 throw new HL7Exception(s.getName() + " is not a group");
122 }
123 return (Group) s;
124 }
125
126 private Structure getStructure(String namePattern, int rep) throws HL7Exception {
127 Structure s = null;
128
129 if (getCurrentStructure(0).equals(this.getRoot()))
130 drillDown(0);
131
132 String[] names = getCurrentStructure(0).getParent().getNames();
133 for (int i = 0; i < names.length && s == null; i++) {
134 if (matches(namePattern, names[i])) {
135 toChild(i);
136 s = getCurrentStructure(rep);
137 }
138 }
139
140 if (s == null)
141 throw new HL7Exception("Can't find " + namePattern + " as a direct child");
142
143 return s;
144 }
145
146 /**
147 * Tests whether the given name matches the given pattern.
148 */
149 /*private boolean matches(String pattern, String candidate) {
150 boolean matches = false;
151 boolean substring = false;
152 if (pattern.substring(0, 1).equals("*")) {
153 substring = true;
154 pattern = pattern.substring(1);
155 }
156
157 if (substring && (candidate.indexOf(pattern) >= 0)) {
158 matches = true;
159 } else if (!substring && candidate.equals(pattern)) {
160 matches = true;
161 }
162 return matches;
163 }*/
164
165 /**
166 * Tests whether the given name matches the given pattern.
167 */
168 private boolean matches(String pattern, String candidate) {
169 //shortcut ...
170 if (pattern.equals(candidate)) {
171 return true;
172 }
173 if (!VALID_PATTERN_PATTERN.matcher(pattern).matches())
174 throw new IllegalArgumentException("The pattern " + pattern + " is not valid. Only [\\w\\*\\?]* allowed.");
175 pattern = LITERAL_UNBOUNDED.matcher(pattern).replaceAll(".*");
176 pattern = LITERAL_OPTIONAL.matcher(pattern).replaceAll(".");
177 return Pattern.matches(pattern, candidate);
178 }
179 }