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