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 Initial Developer of the Original Code is University Health Network. Copyright (C)
010    2001.  All Rights Reserved.
011    
012    Contributor(s): ______________________________________.
013    
014    Alternatively, the contents of this file may be used under the terms of the
015    GNU General Public License (the  ???GPL???), in which case the provisions of the GPL are
016    applicable instead of those above.  If you wish to allow use of your version of this
017    file only under the terms of the GPL and not to allow others to use your version
018    of this file under the MPL, indicate your decision by deleting  the provisions above
019    and replace  them with the notice and other provisions required by the GPL License.
020    If you do not delete the provisions above, a recipient may use your version of
021    this file under either the MPL or the GPL.
022    
023    */
024    package ca.uhn.hl7v2.parser;
025    
026    /**
027     *
028     * @author James
029     */
030    import ca.uhn.hl7v2.HL7Exception;
031    
032    import java.util.Map;
033    import org.apache.commons.logging.Log;
034    import org.apache.commons.logging.LogFactory;
035    import ca.uhn.hl7v2.model.Type;
036    import ca.uhn.hl7v2.model.Segment;
037    import ca.uhn.hl7v2.model.Message;
038    import ca.uhn.hl7v2.model.Group;
039    import java.util.HashMap;
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, DefaultModelClassFactory is used.
045     *
046     * @author Based on implementation by Christian Ohr
047     * @since 1.0
048     */
049    public class CustomModelClassFactory implements ModelClassFactory {
050    
051        private static final long serialVersionUID = 1;
052        private ModelClassFactory defaultFactory;
053        private Map<String, String[]> customModelClasses;
054        private static Log LOG = LogFactory.getLog(CustomModelClassFactory.class);
055    
056        /**
057         * Constructor which just delegated to {@link DefaultModelClassFactory}
058         */
059        public CustomModelClassFactory() {
060            this((Map<String, String[]>)null);
061        }
062    
063    
064        /**
065         * Constructor
066         *
067         * @param packageName The base package name to use.
068         * <p>
069         * When searching, package specified here will be appended with .[version].[structure type].
070         * </p>
071         * <p>
072         * 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>"
073         * </p>
074         */
075        public CustomModelClassFactory(String packageName) {
076            defaultFactory = new DefaultModelClassFactory();
077            customModelClasses = new HashMap<String, String[]>();
078    
079            if (!packageName.endsWith(".")) {
080                packageName += ".";
081            }
082    
083            for (String nextVersion : Parser.getValidVersions()) {
084                final String packageVersion = "v" + nextVersion.replace(".", "");
085                customModelClasses.put(nextVersion, new String[] {packageName + packageVersion});
086            }
087        }
088    
089        
090        /**
091         * Constructor
092         * @param map Map of packages to include.
093         * <p>
094         * Keys are versions of HL7, e.g. "v25".
095         * </p>
096         * <p>
097         * Values are an array of packages to search in for custom model classes.
098         * When searching, the package name here will be appended with "<b>.[structure type]</b>".
099         * So, for example, to specify a custom message type, you could create the class
100         * <code>foo.example.v23.message.ZRM_Z01</code>, and pass in the string "<code>foo.example.v23</code>".
101         * </p>
102         */
103        public CustomModelClassFactory(Map<String, String[]> map) {
104            defaultFactory = new DefaultModelClassFactory();
105            customModelClasses = map;
106        }
107    
108    
109        /**
110         * {@inheritDoc }
111         */
112        public Class<? extends Message> getMessageClass(String name, String version, boolean isExplicit) throws HL7Exception {
113            if (!isExplicit) {
114                name = Parser.getMessageStructureForEvent(name, version);
115            }
116            Class<? extends Message> retVal = (Class<? extends Message>) findClass("message", name, version);
117            if (retVal == null) {
118                retVal = defaultFactory.getMessageClass(name, version, isExplicit);
119            }
120            return retVal;
121        }
122    
123        /**
124         * {@inheritDoc }
125         */
126        public Class<? extends Group> getGroupClass(String name, String version) throws HL7Exception {
127            Class<? extends Group> retVal = (Class<? extends Group>) findClass("group", name, version);
128            if (retVal == null) {
129                retVal = defaultFactory.getGroupClass(name, version);
130            }
131            return retVal;
132        }
133    
134        /**
135         * {@inheritDoc }
136         */
137        public Class<? extends Segment> getSegmentClass(String name, String version) throws HL7Exception {
138            Class<? extends Segment> retVal = (Class<? extends Segment>) findClass("segment", name, version);
139            if (retVal == null) {
140                retVal = defaultFactory.getSegmentClass(name, version);
141            }
142            return retVal;
143        }
144    
145        /**
146         * {@inheritDoc }
147         */
148        public Class<? extends Type> getTypeClass(String name, String version) throws HL7Exception {
149            Class<? extends Type> retVal = (Class<? extends Type>) findClass("datatype", name, version);
150            if (retVal == null) {
151                retVal = defaultFactory.getTypeClass(name, version);
152            }
153            return retVal;
154        }
155    
156        /**
157         * Finds appropriate classes to be loaded for the given structure/type
158         */
159        protected Class<?> findClass(String subpackage, String name, String version) throws HL7Exception {
160            if (!Parser.validVersion(version)) {
161                throw new HL7Exception("HL7 version " + version + " is not supported",
162                        HL7Exception.UNSUPPORTED_VERSION_ID);
163            }
164            Class<?> classLoaded = null;
165            if (customModelClasses != null) {
166                if (customModelClasses.containsKey(version)) {
167                    for (String next : customModelClasses.get(version)) {
168                        if (!next.endsWith(".")) {
169                            next += ".";
170                        }
171                        String fullyQualifiedName = next + subpackage + '.' + name;
172                        try {
173                            classLoaded = Class.forName(fullyQualifiedName);
174                            LOG.debug("Found " + fullyQualifiedName + " in custom HL7 model");
175                        } catch (ClassNotFoundException e) {
176                            // ignore
177                        }
178                    }
179                }
180            }
181            return classLoaded;
182        }
183    }