001package ca.uhn.hl7v2.conf.store;
002
003import java.io.IOException;
004import java.net.URL;
005import java.util.ArrayList;
006import java.util.HashMap;
007import java.util.List;
008import java.util.Map;
009
010import org.xml.sax.Attributes;
011import org.xml.sax.SAXException;
012import org.xml.sax.XMLReader;
013import org.xml.sax.helpers.DefaultHandler;
014import org.xml.sax.helpers.XMLReaderFactory;
015
016import ca.uhn.hl7v2.conf.ProfileException;
017
018/**
019 * 
020 * This particular implementation of CodeStore retrieves valid codes and validates codeSystems using
021 * tables found in 'spec xml tables only' docs generated from the HL7 Messaging Workbench tool.
022 * 
023 * Note: The codeSystem parameter value used in the following methods must be a concatenation of a
024 * coding authority and coding table number that is 4 digits long.
025 * 
026 * Note: The current implementation only accepts a coding authority of HL7
027 * 
028 * @author Neal Acharya
029 * @author Christian Ohr
030 */
031public class ProfileCodeStore extends AbstractCodeStore {
032
033    private Map<String, List<String>> codes = new HashMap<String, List<String>>();
034
035    /**
036     * Creates a ProfileCodeStore object that uses tables found in an 'spec xml tables only' xml doc
037     * specified by the input URI. The private field member tableDoc is created with content from
038     * the xml doc specified by the URI.
039     * 
040     * @param uri the location of the specification XML file
041     * 
042     * @throws ProfileException
043     * @throws IOException
044     * 
045     */
046    public ProfileCodeStore(String uri) throws ProfileException, IOException {
047        try {
048            if (uri == null) {
049                throw new ProfileException("The input url parameter cannot be null");
050            }
051            XMLReader reader = XMLReaderFactory.createXMLReader();
052            reader.setContentHandler(new ProfileCodeStoreHandler());
053            reader.parse(uri);
054        } catch (IOException e) {
055            throw e;
056        } catch (Exception e) {
057            throw new ProfileException(e.toString(), e);
058        }
059    }
060
061    /** As string constructor but accepts a URL object */
062    public ProfileCodeStore(URL url) throws ProfileException, IOException {
063        this(url.toExternalForm());
064    }
065
066    /**
067     * Retrieves all codes for a given conformance profile and codeSystem. Note: The codeSystem
068     * parameter value must be a concatenation of a coding authority and coding table number that is
069     * 4 digits long.
070     * 
071     * Note: The current implementation only accepts a coding authority of HL7
072     * 
073     * @param codeSystem
074     * @return String[]
075     * @throws ProfileException
076     * @see ca.uhn.hl7v2.conf.store.CodeStore#getValidCodes(java.lang.String, java.lang.String)
077     * 
078     */
079    public String[] getValidCodes(String codeSystem) throws ProfileException {
080        List<String> result = getCodeTable(codeSystem);
081        if (result == null)
082            throw new ProfileException("Unknown code system: " + codeSystem);
083        return result.toArray(new String[result.size()]);
084    }
085
086    /**
087     * Validates the codeSystem against the input conformance profile. If valid then output is
088     * 'true' else 'false'. Note: The codeSystem parameter value must be a concatenation of a coding
089     * authority and coding table number that is 4 digits long.
090     * 
091     * Note: The current implementation only accepts a coding authority of HL7
092     * 
093     * @param codeSystem
094     * 
095     * @return boolean
096     * @see ca.uhn.hl7v2.conf.store.CodeStore#knowsCodes(java.lang.String, java.lang.String)
097     * 
098     */
099    public boolean knowsCodes(String codeSystem) {
100        try {
101            return getCodeTable(codeSystem) != null;
102        } catch (ProfileException e) {
103            return false;
104        }
105    }
106
107    /**
108     * Retrieves the hl7Table Element from the tableDoc object defined by the table number in the
109     * input codeSystem.
110     * 
111     * @param profileId
112     * 
113     * @param codeSystem
114     * @return Element
115     * @throws ProfileException Element
116     */
117    private List<String> getCodeTable(String codeSystem) throws ProfileException {
118        if (codeSystem == null) {
119            throw new ProfileException("The input codeSystem parameter cannot be null");
120        }
121        if (codeSystem.length() < 4) {
122            throw new ProfileException("The input codeSystem parameter cannot be less than 4 characters long");
123        }
124        // Extract the last 4 characters from the codeSystem param
125        String tableId = codeSystem.substring(codeSystem.length() - 4);
126        return codes.get(tableId);
127    }
128
129    private class ProfileCodeStoreHandler extends DefaultHandler {
130
131        private String currentTable;
132        private static final String HL7_TABLE_QNAME = "hl7table";
133        private static final String TABLE_ELEMENT_QNAME = "tableElement";
134
135        @Override
136        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
137            if (HL7_TABLE_QNAME.equals(qName)) {
138                currentTable = attributes.getValue("id");
139                codes.put(currentTable, new ArrayList<String>());
140            } else if (TABLE_ELEMENT_QNAME.equals(qName)) {
141                codes.get(currentTable).add(attributes.getValue("code"));
142            }
143        }
144
145    }
146}