001/** 002The 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. 004You may obtain a copy of the License at http://www.mozilla.org/MPL/ 005Software distributed under the License is distributed on an "AS IS" basis, 006WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the 007specific language governing rights and limitations under the License. 008 009The Initial Developer of the Original Code is University Health Network. Copyright (C) 0102001. All Rights Reserved. 011 012Contributor(s): ______________________________________. 013 014Alternatively, the contents of this file may be used under the terms of the 015GNU General Public License (the �GPL�), in which case the provisions of the GPL are 016applicable instead of those above. If you wish to allow use of your version of this 017file only under the terms of the GPL and not to allow others to use your version 018of this file under the MPL, indicate your decision by deleting the provisions above 019and replace them with the notice and other provisions required by the GPL License. 020If you do not delete the provisions above, a recipient may use your version of 021this file under either the MPL or the GPL. 022 023*/ 024package ca.uhn.hl7v2.parser; 025 026import java.util.HashMap; 027import java.util.Map; 028import java.util.Map.Entry; 029 030import org.slf4j.Logger; 031import org.slf4j.LoggerFactory; 032 033import ca.uhn.hl7v2.HL7Exception; 034import ca.uhn.hl7v2.Version; 035import ca.uhn.hl7v2.model.Group; 036import ca.uhn.hl7v2.model.Message; 037import ca.uhn.hl7v2.model.Segment; 038import ca.uhn.hl7v2.model.Type; 039import ca.uhn.hl7v2.util.StringUtil; 040 041/** 042 * ModelClassFactory which allows custom packages to search to be specified. 043 * These packages will be searched first, and if nothing is found for a particular 044 * structure, a delegate ModelClassFactory (by default DefaultModelClassFactory) is used. 045 * <p> 046 * Also, a custom event map location is supported (by setting {@link #setEventMapDirectory(String)} 047 * in a way that only new or modified structures needs to be defined there. If no structure was 048 * found, the event map of the delegate ModelClassFactory serves as fallback. 049 * 050 * @author Christian Ohr 051 * @author James Agnew 052 * @since 1.0 053 */ 054public class CustomModelClassFactory extends AbstractModelClassFactory { 055 056 private static final long serialVersionUID = 1; 057 private static Logger LOG = LoggerFactory.getLogger(CustomModelClassFactory.class); 058 059 private final ModelClassFactory delegate; 060 private Map<String, String[]> customModelClasses; 061 062 /** 063 * Constructor which just delegated to {@link DefaultModelClassFactory} 064 */ 065 public CustomModelClassFactory() { 066 this((Map<String, String[]>)null); 067 } 068 069 070 /** 071 * Constructor 072 * 073 * @param packageName The base package name to use. 074 * <p> 075 * When searching, package specified here will be appended with .[version].[structure type]. 076 * </p> 077 * <p> 078 * 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>" 079 * </p> 080 */ 081 public CustomModelClassFactory(String packageName) { 082 this(new HashMap<String, String[]>()); 083 084 if (!packageName.endsWith(".")) { 085 packageName += "."; 086 } 087 for (Version v : Version.values()) { 088 addModel(v.getVersion(), new String[] {packageName + v.getPackageVersion()}); 089 } 090 } 091 092 093 /** 094 * Constructor 095 * @param map Map of packages to include. 096 * <p> 097 * Keys are versions of HL7, e.g. "2.5". 098 * </p> 099 * <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}