/** * Copyright (c) 2010-2016 by the respective copyright holders. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package org.openhab.binding.insteonplm.internal.device; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Map.Entry; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.openhab.binding.insteonplm.internal.device.DeviceType.FeatureGroup; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; /** * Reads the device types from an xml file. * * @author Daniel Pfrommer * @author Bernd Pfrommer * @since 1.5.0 */ public class DeviceTypeLoader { private static final Logger logger = LoggerFactory.getLogger(DeviceTypeLoader.class); private HashMap<String, DeviceType> m_deviceTypes = new HashMap<String, DeviceType>(); private static DeviceTypeLoader s_deviceTypeLoader = null; private DeviceTypeLoader() { } // private so nobody can call it /** * Finds the device type for a given product key * * @param aProdKey product key to search for * @return the device type, or null if not found */ public DeviceType getDeviceType(String aProdKey) { return (m_deviceTypes.get(aProdKey)); } /** * Must call loadDeviceTypesXML() before calling this function! * * @return currently known device types */ public HashMap<String, DeviceType> getDeviceTypes() { return (m_deviceTypes); } /** * Reads the device types from input stream and stores them in memory for * later access. * * @param is the input stream from which to read */ public void loadDeviceTypesXML(InputStream in) throws ParserConfigurationException, SAXException, IOException { DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); Document doc = dBuilder.parse(in); doc.getDocumentElement().normalize(); Node root = doc.getDocumentElement(); NodeList nodes = root.getChildNodes(); for (int i = 0; i < nodes.getLength(); i++) { Node node = nodes.item(i); if (node.getNodeType() == Node.ELEMENT_NODE && node.getNodeName().equals("device")) { processDevice((Element) node); } } } /** * Reads the device types from file and stores them in memory for later access. * * @param aFileName The name of the file to read from * @throws ParserConfigurationException * @throws SAXException * @throws IOException */ public void loadDeviceTypesXML(String aFileName) throws ParserConfigurationException, SAXException, IOException { File file = new File(aFileName); InputStream in = new FileInputStream(file); loadDeviceTypesXML(in); } /** * Process device node * * @param e name of the element to process * @throws SAXException */ private void processDevice(Element e) throws SAXException { String productKey = e.getAttribute("productKey"); if (productKey.equals("")) { throw new SAXException("device in device_types file has no product key!"); } if (m_deviceTypes.containsKey(productKey)) { logger.warn("overwriting previous definition of device {}", productKey); m_deviceTypes.remove(productKey); } DeviceType devType = new DeviceType(productKey); NodeList nodes = e.getChildNodes(); for (int i = 0; i < nodes.getLength(); i++) { Node node = nodes.item(i); if (node.getNodeType() != Node.ELEMENT_NODE) { continue; } Element subElement = (Element) node; if (subElement.getNodeName().equals("model")) { devType.setModel(subElement.getTextContent()); } else if (subElement.getNodeName().equals("description")) { devType.setDescription(subElement.getTextContent()); } else if (subElement.getNodeName().equals("feature")) { processFeature(devType, subElement); } else if (subElement.getNodeName().equals("feature_group")) { processFeatureGroup(devType, subElement); } m_deviceTypes.put(productKey, devType); } } private String processFeature(DeviceType devType, Element e) throws SAXException { String name = e.getAttribute("name"); if (name.equals("")) { throw new SAXException("feature " + e.getNodeName() + " has feature without name!"); } if (!devType.addFeature(name, e.getTextContent())) { throw new SAXException("duplicate feature: " + name); } return (name); } private String processFeatureGroup(DeviceType devType, Element e) throws SAXException { String name = e.getAttribute("name"); if (name.equals("")) { throw new SAXException("feature group " + e.getNodeName() + " has no name attr!"); } String type = e.getAttribute("type"); if (type.equals("")) { throw new SAXException("feature group " + e.getNodeName() + " has no type attr!"); } FeatureGroup fg = new FeatureGroup(name, type); NodeList nodes = e.getChildNodes(); for (int i = 0; i < nodes.getLength(); i++) { Node node = nodes.item(i); if (node.getNodeType() != Node.ELEMENT_NODE) { continue; } Element subElement = (Element) node; if (subElement.getNodeName().equals("feature")) { fg.addFeature(processFeature(devType, subElement)); } else if (subElement.getNodeName().equals("feature_group")) { fg.addFeature(processFeatureGroup(devType, subElement)); } } if (!devType.addFeatureGroup(name, fg)) { throw new SAXException("duplicate feature group " + name); } return (name); } /** * Helper function for debugging */ private void logDeviceTypes() { for (Entry<String, DeviceType> dt : getDeviceTypes().entrySet()) { logger.debug(String.format("%-10s->", dt.getKey()) + dt.getValue()); } } /** * Singleton instance function, creates DeviceTypeLoader * * @return DeviceTypeLoader singleton reference */ public static synchronized DeviceTypeLoader s_instance() { if (s_deviceTypeLoader == null) { s_deviceTypeLoader = new DeviceTypeLoader(); InputStream input = DeviceTypeLoader.class.getResourceAsStream("/device_types.xml"); try { s_deviceTypeLoader.loadDeviceTypesXML(input); } catch (ParserConfigurationException e) { logger.error("parser config error when reading device types xml file: ", e); } catch (SAXException e) { logger.error("SAX exception when reading device types xml file: ", e); } catch (IOException e) { logger.error("I/O exception when reading device types xml file: ", e); } logger.debug("loaded {} devices: ", s_deviceTypeLoader.getDeviceTypes().size()); s_deviceTypeLoader.logDeviceTypes(); } return s_deviceTypeLoader; } /** * Test function for debugging */ public static void main(String[] arg) throws Exception { String fileName = System.getProperty("user.home") + "/workspace/openhab/bundles/binding/org.openhab.binding.insteonplm/target/classes/device_types.xml"; try { DeviceTypeLoader dtl = s_instance(); dtl.loadDeviceTypesXML(fileName); for (Entry<String, DeviceType> dt : dtl.getDeviceTypes().entrySet()) { System.out.println(String.format("%-10s ->", dt.getKey()) + dt.getValue()); } } catch (SAXException e) { System.out.println("got exception: " + e); } } }