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