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 }