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*/
023package ca.uhn.hl7v2.parser;
024
025import java.io.IOException;
026import java.io.InputStream;
027import java.util.Collections;
028import java.util.HashMap;
029import java.util.Map;
030import java.util.Properties;
031
032import ca.uhn.hl7v2.HL7Exception;
033import ca.uhn.hl7v2.Version;
034import org.slf4j.Logger;
035import org.slf4j.LoggerFactory;
036
037/**
038 * Abstract base class for {@link ModelClassFactory} implementations that read event maps from the
039 * file system.
040 * <p>
041 * The directory can be set using {@link #setEventMapDirectory(String)} and defaults to
042 * <code>ca/uhn/hl7v2/parser/eventmap/</code>. The file itself is a property file named after the
043 * HL7 version (e.g. <code>2.4.properties</code>).
044 * </p>
045 * 
046 * 
047 * @author Christian Ohr
048 */
049@SuppressWarnings("serial")
050public abstract class AbstractModelClassFactory implements ModelClassFactory {
051
052        protected static final String DEFAULT_EVENT_MAP_DIRECTORY = "ca/uhn/hl7v2/parser/eventmap/";
053        private static final Logger LOG = LoggerFactory.getLogger(AbstractModelClassFactory.class);
054
055        private String eventMapDirectory = DEFAULT_EVENT_MAP_DIRECTORY;
056        private Map<Version, Map<String, String>> eventMap;
057
058
059        /**
060         * @return the directory where to read the eventmap file from
061         */
062        public String getEventMapDirectory() {
063                return eventMapDirectory;
064        }
065
066        /**
067         * @param eventMapPrefix the directory where to read the eventmap file from
068         */
069        public void setEventMapDirectory(String eventMapPrefix) {
070                this.eventMapDirectory = eventMapPrefix;
071        }
072
073        /**
074         * @see ca.uhn.hl7v2.parser.ModelClassFactory#getMessageStructureForEvent(java.lang.String,
075         *      ca.uhn.hl7v2.Version)
076         */
077        public String getMessageStructureForEvent(String name, Version version) throws HL7Exception {
078                Map<String, String> p = getEventMapForVersion(version);
079                if (p == null) {
080                        // Instead of throwing an Exception, Allow to parse as generic message if the structure library
081                        // (and the contained event map) are not on the classpath.
082                        LOG.debug("No event map found for version " + version);
083                        return name;
084                        // before:
085                        // throw new HL7Exception("No map found for version " + version
086                        //              + ". Only the following are available: " + getEventMap().keySet());
087                } else {
088                        return p.get(name);
089                }
090        }
091
092        /**
093         * Returns the event map for a given HL7 version. In this map, the key is a message
094         * type and trigger event in the form <code>[type]_[trigger]</code>, for example:
095         * <code>ADT_A04</code>, and the values are the corresponding structure for this trigger,
096         * for example: <code>ADT_A01</code>.
097         *
098     * @param version the HL7 version
099         * @return Returns <code>null</code> if no event map is found for the given version
100     * @throws HL7Exception if the HL7 version is unknown
101         */
102        public Map<String, String> getEventMapForVersion(Version version) throws HL7Exception {
103                return getEventMap().get(version);
104        }
105
106        /**
107         * Initializes the event map once and returns it.
108         * <p>
109         * This method is package private for testing reasons.
110         *  
111         * @return the event map
112         * @throws HL7Exception
113         */
114        synchronized Map<Version, Map<String, String>> getEventMap() throws HL7Exception {
115                if (eventMap == null) {
116                        try {
117                                eventMap = loadMessageStructures();
118                        } catch (IOException e) {
119                                throw new HL7Exception("Could not load event map", e);
120                        }
121                }
122                return eventMap;
123        }
124
125        /**
126         * Load event map from a external resource
127         * 
128         * @return the event map
129         * @throws IOException
130         */
131        protected Map<Version, Map<String, String>> loadMessageStructures() throws IOException {
132                Map<Version, Map<String, String>> map = new HashMap<Version, Map<String, String>>();
133                for (Version v : Version.values()) {
134                        String resource = getEventMapDirectory() + v.getVersion() + ".properties";
135                        InputStream in = getResource(resource);
136                        if (in != null) {
137                                try {
138                                        Properties structures = new Properties();
139                                        structures.load(in);
140                                        
141                                        Map<String, String> structureMap = new HashMap<String, String>();
142                                        for(Map.Entry<Object, Object> next : structures.entrySet()) {
143                                                structureMap.put((String)next.getKey(), (String)next.getValue());
144                                        }
145                                        
146                                        map.put(v, Collections.unmodifiableMap(structureMap));
147                                } finally {
148                                        in.close();
149                                }
150                        }
151                }
152                return map;
153        }
154
155        private InputStream getResource(String resource) {
156                InputStream in = null;
157                ClassLoader loader = Thread.currentThread().getContextClassLoader();
158                if (loader != null) {
159                        in = loader.getResourceAsStream(resource);
160                }
161                if (in == null) {
162                        loader = AbstractModelClassFactory.class.getClassLoader();
163                        if (loader != null) {
164                                in = loader.getResourceAsStream(resource);
165                        }
166                }
167                if (in == null) {
168                        in = ClassLoader.getSystemResourceAsStream(resource);
169                }
170                return in;
171        }
172
173}