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 028package ca.uhn.hl7v2.util; 029 030import ca.uhn.hl7v2.model.*; 031import ca.uhn.hl7v2.HL7Exception; 032import 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 */ 038public class SegmentFinder extends MessageNavigator { 039 040 private static final Pattern VALID_PATTERN_PATTERN = Pattern.compile("[\\w\\*\\?]*"); 041 private static final Pattern LITERAL_UNBOUNDED = Pattern.compile("\\*"); 042 private static final Pattern LITERAL_OPTIONAL = Pattern.compile("\\?"); 043 044 /** 045 * Creates a new instance of SegmentFinder. 046 * @param root the scope of searches -- may be a whole message or only a branch 047 */ 048 public SegmentFinder(Group root) { 049 super(root); 050 } 051 052 /** 053 * Returns the first segment with a name that matches the given pattern, in a depth-first search. 054 * Repeated searches are initiated from the location just AFTER where the last segment was found. 055 * Call reset() is this is not desired. Note: this means that the current location will not be found. 056 * @param namePattern the name of the segment to find. The wildcard * means any number 057 * of arbitrary characters; the wildcard ? one arbitrary character 058 * (eg "P*" or "*ID" or "???" or "P??" would match on PID). 059 * @param rep the repetition of the segment to return 060 */ 061 public Segment findSegment(String namePattern, int rep) throws HL7Exception { 062 Structure s = null; 063 do { 064 s = findStructure(namePattern, rep); 065 } while (!Segment.class.isAssignableFrom(s.getClass())); 066 return (Segment) s; 067 } 068 069 /** 070 * As findSegment(), but will only return a group. 071 */ 072 public Group findGroup(String namePattern, int rep) throws HL7Exception { 073 Structure s; 074 do { 075 s = findStructure(namePattern, rep); 076 } while (!Group.class.isAssignableFrom(s.getClass())); 077 return (Group) s; 078 } 079 080 /** 081 * Returns the first matching structure AFTER the current position 082 */ 083 private Structure findStructure(String namePattern, int rep) throws HL7Exception { 084 Structure s = null; 085 086 while (s == null) { 087 String currentNameInParent = iterate(false, false); 088 String currentName = getCurrentStructure(0).getName(); 089 if (matches(namePattern, currentName) || matches(namePattern, currentNameInParent)) { 090 s = getCurrentStructure(rep); 091 } 092 } 093 return s; 094 } 095 096 /** 097 * Returns the first segment with a name matching the given pattern that is a sibling of 098 * the structure at the current location. Other parts of the message are 099 * 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}