View Javadoc
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 }