001 /**
002 * The contents of this file are subject to the Mozilla Public License Version 1.1
003 * (the "License"); you may not use this file except in compliance with the License.
004 * You may obtain a copy of the License at http://www.mozilla.org/MPL/
005 * Software distributed under the License is distributed on an "AS IS" basis,
006 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the
007 * specific language governing rights and limitations under the License.
008 *
009 * The Original Code is "SegmentFinder.java". Description:
010 * "A tool for getting segments by name within a message or part of a message."
011 *
012 * The Initial Developer of the Original Code is University Health Network. Copyright (C)
013 * 2002. All Rights Reserved.
014 *
015 * Contributor(s): ______________________________________.
016 *
017 * Alternatively, the contents of this file may be used under the terms of the
018 * GNU General Public License (the "GPL"), in which case the provisions of the GPL are
019 * applicable instead of those above. If you wish to allow use of your version of this
020 * file only under the terms of the GPL and not to allow others to use your version
021 * of this file under the MPL, indicate your decision by deleting the provisions above
022 * and replace them with the notice and other provisions required by the GPL License.
023 * If you do not delete the provisions above, a recipient may use your version of
024 * this file under either the MPL or the GPL.
025 *
026 */
027
028 package ca.uhn.hl7v2.util;
029
030 import ca.uhn.hl7v2.model.*;
031 import ca.uhn.hl7v2.HL7Exception;
032 import java.util.regex.*;
033
034 /**
035 * A tool for getting segments by name within a message or part of a message.
036 * @author Bryan Tripp
037 */
038 public class SegmentFinder extends MessageNavigator {
039
040 /**
041 * Creates a new instance of SegmentFinder.
042 * @param root the scope of searches -- may be a whole message or only a branch
043 */
044 public SegmentFinder(Group root) {
045 super(root);
046 }
047
048 /**
049 * Returns the first segment with a name that matches the given pattern, in a depth-first search.
050 * Repeated searches are initiated from the location just AFTER where the last segment was found.
051 * Call reset() is this is not desired. Note: this means that the current location will not be found.
052 * @param segmentName the name of the segment to find. The wildcard * means any number
053 * of arbitrary characters; the wildcard ? one arbitrary character
054 * (eg "P*" or "*ID" or "???" or "P??" would match on PID).
055 * @param rep the repetition of the segment to return
056 */
057 public Segment findSegment(String namePattern, int rep) throws HL7Exception {
058 Structure s = null;
059 do {
060 s = findStructure(namePattern, rep);
061 } while (!Segment.class.isAssignableFrom(s.getClass()));
062 return (Segment) s;
063 }
064
065 /**
066 * As findSegment(), but will only return a group.
067 */
068 public Group findGroup(String namePattern, int rep) throws HL7Exception {
069 Structure s = null;
070 do {
071 s = findStructure(namePattern, rep);
072 } while (!Group.class.isAssignableFrom(s.getClass()));
073 return (Group) s;
074 }
075
076 /**
077 * Returns the first matching structure AFTER the current position
078 */
079 private Structure findStructure(String namePattern, int rep) throws HL7Exception {
080 Structure s = null;
081
082 while (s == null) {
083 iterate(false, false);
084 String currentName = getCurrentStructure(0).getName();
085 if (matches(namePattern, currentName)) {
086 s = getCurrentStructure(rep);
087 }
088 }
089 return s;
090 }
091
092 /**
093 * Returns the first segment with a name matching the given pattern that is a sibling of
094 * the structure at the current location. Other parts of the message are
095 * not searched (in contrast to findSegment).
096 * As a special case, if the pointer is at the root, the children of the root
097 * are searched.
098 * @param segmentName the name of the segment to get. The wildcad * means any number
099 * of arbitrary characters; the wildard ? one arbitrary character
100 * (eg "P*" or "*ID" or "???" or "P??" would match on PID).
101 * @param rep the repetition of the segment to return
102 */
103 public Segment getSegment(String namePattern, int rep) throws HL7Exception {
104 Structure s = getStructure(namePattern, rep);
105 if (!Segment.class.isAssignableFrom(s.getClass())) {
106 throw new HL7Exception(s.getName() + " is not a segment", HL7Exception.APPLICATION_INTERNAL_ERROR);
107 }
108 return (Segment) s;
109 }
110
111 /**
112 * As getSegment() but will only return a group.
113 */
114 public Group getGroup(String namePattern, int rep) throws HL7Exception {
115 Structure s = getStructure(namePattern, rep);
116 if (!Group.class.isAssignableFrom(s.getClass())) {
117 throw new HL7Exception(s.getName() + " is not a group", HL7Exception.APPLICATION_INTERNAL_ERROR);
118 }
119 return (Group) s;
120 }
121
122 private Structure getStructure(String namePattern, int rep) throws HL7Exception {
123 Structure s = null;
124
125 if (getCurrentStructure(0).equals(this.getRoot()))
126 drillDown(0);
127
128 String[] names = getCurrentStructure(0).getParent().getNames();
129 for (int i = 0; i < names.length && s == null; i++) {
130 if (matches(namePattern, names[i])) {
131 toChild(i);
132 s = getCurrentStructure(rep);
133 }
134 }
135
136 if (s == null)
137 throw new HL7Exception("Can't find " + namePattern + " as a direct child", HL7Exception.APPLICATION_INTERNAL_ERROR);
138
139 return s;
140 }
141
142 /**
143 * Tests whether the given name matches the given pattern.
144 */
145 /*private boolean matches(String pattern, String candidate) {
146 boolean matches = false;
147 boolean substring = false;
148 if (pattern.substring(0, 1).equals("*")) {
149 substring = true;
150 pattern = pattern.substring(1);
151 }
152
153 if (substring && (candidate.indexOf(pattern) >= 0)) {
154 matches = true;
155 } else if (!substring && candidate.equals(pattern)) {
156 matches = true;
157 }
158 return matches;
159 }*/
160
161 /**
162 * Tests whether the given name matches the given pattern.
163 */
164 private boolean matches(String pattern, String candidate) {
165 //shortcut ...
166 if (pattern.equals(candidate)) {
167 return true;
168 }
169
170 if (!Pattern.matches("[\\w\\*\\?]*", pattern))
171 throw new IllegalArgumentException("The pattern " + pattern + " is not valid. Only [\\w\\*\\?]* allowed.");
172
173 pattern = Pattern.compile("\\*").matcher(pattern).replaceAll(".*");
174 pattern = Pattern.compile("\\?").matcher(pattern).replaceAll(".");
175
176 return Pattern.matches(pattern, candidate);
177 }
178 }