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 Initial Developer of the Original Code is University Health Network. Copyright (C)
10  2001.  All Rights Reserved.
11  
12  Contributor(s): ______________________________________.
13  
14  Alternatively, the contents of this file may be used under the terms of the
15  GNU General Public License (the  �GPL�), in which case the provisions of the GPL are
16  applicable instead of those above.  If you wish to allow use of your version of this
17  file only under the terms of the GPL and not to allow others to use your version
18  of this file under the MPL, indicate your decision by deleting  the provisions above
19  and replace  them with the notice and other provisions required by the GPL License.
20  If you do not delete the provisions above, a recipient may use your version of
21  this file under either the MPL or the GPL.
22  
23  */
24  package ca.uhn.hl7v2.parser;
25  
26  import java.util.HashMap;
27  import java.util.Map;
28  import java.util.Map.Entry;
29  
30  import org.slf4j.Logger;
31  import org.slf4j.LoggerFactory;
32  
33  import ca.uhn.hl7v2.HL7Exception;
34  import ca.uhn.hl7v2.Version;
35  import ca.uhn.hl7v2.model.Group;
36  import ca.uhn.hl7v2.model.Message;
37  import ca.uhn.hl7v2.model.Segment;
38  import ca.uhn.hl7v2.model.Type;
39  import ca.uhn.hl7v2.util.StringUtil;
40  
41  /**
42   * ModelClassFactory which allows custom packages to search to be specified.
43   * These packages will be searched first, and if nothing is found for a particular
44   * structure, a delegate ModelClassFactory (by default DefaultModelClassFactory) is used. 
45   * <p>
46   * Also, a custom event map location is supported (by setting {@link #setEventMapDirectory(String)}
47   * in a way that only new or modified structures needs to be defined there. If no structure was
48   * found, the event map of the delegate ModelClassFactory serves as fallback.
49   *
50   * @author Christian Ohr
51   * @author James Agnew
52   * @since 1.0
53   */
54  public class CustomModelClassFactory extends AbstractModelClassFactory {
55  
56      private static final long serialVersionUID = 1;
57      private static Logger LOG = LoggerFactory.getLogger(CustomModelClassFactory.class);
58  
59      private final ModelClassFactory delegate;
60      private Map<String, String[]> customModelClasses;
61  
62      /**
63       * Constructor which just delegated to {@link DefaultModelClassFactory}
64       */
65      public CustomModelClassFactory() {
66          this((Map<String, String[]>)null);
67      }
68  
69  
70      /**
71       * Constructor
72       *
73       * @param packageName The base package name to use.
74       * <p>
75       * When searching, package specified here will be appended with .[version].[structure type].
76       * </p>
77       * <p>
78       * So, for instance, when looking for a v2.5 segment object, if "<code>com.foo</code>" is passed in, HAPI will look in "<code>com.foo.v25.segment.*</code>"
79       * </p>
80       */
81      public CustomModelClassFactory(String packageName) {
82          this(new HashMap<String, String[]>());
83  
84          if (!packageName.endsWith(".")) {
85              packageName += ".";
86          }
87          for (Version v : Version.values()) {
88          	addModel(v.getVersion(), new String[] {packageName + v.getPackageVersion()});
89          }
90      }
91  
92      
93      /**
94       * Constructor
95       * @param map Map of packages to include.
96       * <p>
97       * Keys are versions of HL7, e.g. "2.5".
98       * </p>
99       * <p>
100      * Values are an array of packages to search in for custom model classes.
101      * When searching, the package name here will be appended with "<b>.[structure type]</b>".
102      * So, for example, to specify a custom message type, you could create the class
103      * <code>foo.example.v23.message.ZRM_Z01</code>, and pass in the string "<code>foo.example.v23</code>".
104      * </p>
105      */
106     public CustomModelClassFactory(Map<String, String[]> map) {
107         this(new DefaultModelClassFactory(), map);
108     }
109     
110     /**
111      * Set an explicit {@link ModelClassFactory} is underlying delegate
112      * @param defaultFactory default factory to be delegated to
113      * @param map custom model map
114      */
115     public CustomModelClassFactory(ModelClassFactory defaultFactory, Map<String, String[]> map) {
116         this.delegate = defaultFactory;
117         customModelClasses = map;
118     }    
119 
120     /**
121      * {@inheritDoc }
122      */
123 	public Class<? extends Message> getMessageClass(String name, String version, boolean isExplicit) throws HL7Exception {
124         if (!isExplicit) {
125             name = getMessageStructureForEvent(name, Version.versionOf(version));
126         }
127         Class<? extends Message> retVal = findClass("message", name, version);
128         if (retVal == null) {
129             retVal = delegate.getMessageClass(name, version, isExplicit);
130         }
131         return retVal;
132     }
133 
134     /**
135      * {@inheritDoc }
136      */
137 	public Class<? extends Group> getGroupClass(String name, String version) throws HL7Exception {
138         Class<? extends Group> retVal = findClass("group", name, version);
139         if (retVal == null) {
140             retVal = delegate.getGroupClass(name, version);
141         }
142         return retVal;
143     }
144 
145     /**
146      * {@inheritDoc }
147      */
148 	public Class<? extends Segment> getSegmentClass(String name, String version) throws HL7Exception {
149         Class<? extends Segment> retVal = findClass("segment", name, version);
150         if (retVal == null) {
151             retVal = delegate.getSegmentClass(name, version);
152         }
153         return retVal;
154     }
155 
156     /**
157      * {@inheritDoc }
158      */
159 	public Class<? extends Type> getTypeClass(String name, String version) throws HL7Exception {
160         Class<? extends Type> retVal = findClass("datatype", name, version);
161         if (retVal == null) {
162             retVal = delegate.getTypeClass(name, version);
163         }
164         return retVal;
165     }
166 
167     /**
168      * Finds appropriate classes to be loaded for the given structure/type
169      */
170     @SuppressWarnings("unchecked")
171 	protected <T> Class<T> findClass(String subpackage, String name, String version) throws HL7Exception {
172         Parser.assertVersionExists(version);
173         Class<T> classLoaded = null;
174         if (customModelClasses != null) {
175             if (customModelClasses.containsKey(version)) {
176                 for (String next : customModelClasses.get(version)) {
177                     if (!next.endsWith(".")) {
178                         next += ".";
179                     }
180                     String fullyQualifiedName = next + subpackage + '.' + name;
181                     try {
182                         classLoaded = (Class<T>) Class.forName(fullyQualifiedName);
183                         LOG.debug("Found " + fullyQualifiedName + " in custom HL7 model");
184                     } catch (ClassNotFoundException e) {
185                         // ignore
186                     }
187                 }
188             }
189         }
190         return classLoaded;
191     }
192 
193     /**
194      * Delegates calls to {@link DefaultModelClassFactory#getMessageClassInASpecificPackage(String, String, boolean, String)}
195      */
196 	public Class<? extends Message> getMessageClassInASpecificPackage(String theName, String theVersion, boolean theIsExplicit, String thePackageName) throws HL7Exception {
197 		return delegate.getMessageClassInASpecificPackage(theName, theVersion, theIsExplicit, thePackageName);
198 	}
199 
200     /**
201      * Returns the configured custom model classes
202      * @return a map of custom model classes
203      */
204     public Map<String, String[]> getCustomModelClasses() {
205 		return customModelClasses;
206 	}
207 
208 	/**
209 	 * Add model class packages after the object has been instantiated
210 	 * 
211 	 * @param addedModelClasses map with version number as key and package names has value
212 	 */
213 	public void addModels(Map<String, String[]> addedModelClasses) {
214         if (customModelClasses == null) {
215         	customModelClasses = new HashMap<String, String[]>();
216         }
217         for (Entry<String, String[]> entry : addedModelClasses.entrySet()) {
218         	addModel(entry.getKey(), entry.getValue());
219         }
220     }
221 	
222 	private void addModel(String version, String[] newPackageNames) {
223         if (customModelClasses.containsKey(version)) {
224             // the new packages must be added after the existing ones.
225             String[] existingPackageNames = customModelClasses.get(version);
226             customModelClasses.put(version, StringUtil.concatenate(existingPackageNames, newPackageNames));
227         } else {
228         	customModelClasses.put(version, newPackageNames);
229         }		
230 	}
231 
232 
233 	/**
234 	 * Looks up its own event map. If no structure was found, the call is delegated to
235 	 * the default ModelClassFactory. If nothing can be found, the eventName is returned
236 	 * as structure.
237 	 * 
238 	 * @see ca.uhn.hl7v2.parser.AbstractModelClassFactory#getMessageStructureForEvent(java.lang.String, ca.uhn.hl7v2.Version)
239 	 */
240 	@Override
241 	public String getMessageStructureForEvent(String eventName, Version version) throws HL7Exception {
242 		String structure = super.getMessageStructureForEvent(eventName, version);
243 		if (structure == null) {
244 			structure = delegate.getMessageStructureForEvent(eventName, version);
245 		}
246 		if (structure != null) {
247 			structure = eventName;
248 		}
249 		return structure;
250 	}
251 	
252 	
253 	
254 }