/** * 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.message; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.LinkedHashMap; 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.utils.Pair; import org.openhab.binding.insteonplm.internal.utils.Utils.DataTypeParser; import org.openhab.binding.insteonplm.internal.utils.Utils.ParsingException; 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 Msg definitions from an XML file * * @author Daniel Pfrommer * @since 1.5.0 */ public class XMLMessageReader { /** * Reads the message definitions from an xml file * * @param input input stream from which to read * @return what was read from file: the map between clear text string and Msg objects * @throws IOException couldn't read file etc * @throws ParsingException something wrong with the file format * @throws FieldException something wrong with the field definition */ public static HashMap<String, Msg> s_readMessageDefinitions(InputStream input) throws IOException, ParsingException, FieldException { HashMap<String, Msg> messageMap = new HashMap<String, Msg>(); try { DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); // Parse it! Document doc = dBuilder.parse(input); 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) { if (node.getNodeName().equals("msg")) { Pair<String, Msg> msgDef = s_readMessageDefinition((Element) node); messageMap.put(msgDef.getKey(), msgDef.getValue()); } } } } catch (SAXException e) { throw new ParsingException("Failed to parse XML!", e); } catch (ParserConfigurationException e) { throw new ParsingException("Got parser config exception! ", e); } return messageMap; } private static Pair<String, Msg> s_readMessageDefinition(Element msg) throws FieldException, ParsingException { int length = 0; int hlength = 0; LinkedHashMap<Field, Object> fieldMap = new LinkedHashMap<Field, Object>(); String dir = msg.getAttribute("direction"); String name = msg.getAttribute("name"); Msg.Direction direction = Msg.Direction.s_getDirectionFromString(dir); if (msg.hasAttribute("length")) { length = Integer.parseInt(msg.getAttribute("length")); } NodeList nodes = msg.getChildNodes(); int offset = 0; for (int i = 0; i < nodes.getLength(); i++) { Node node = nodes.item(i); if (node.getNodeType() == Node.ELEMENT_NODE) { if (node.getNodeName().equals("header")) { int o = s_readHeaderElement((Element) node, fieldMap); hlength = o; // Increment the offset by the header length offset += o; } else { Pair<Field, Object> field = s_readField((Element) node, offset); fieldMap.put(field.getKey(), field.getValue()); // Increment the offset offset += field.getKey().getType().getSize(); } } } if (offset != length) { throw new ParsingException( "Actual msg length " + offset + " differs from given msg length " + length + "!"); } if (length == 0) { length = offset; } return new Pair<String, Msg>(name, s_createMsg(fieldMap, length, hlength, direction)); } private static int s_readHeaderElement(Element header, LinkedHashMap<Field, Object> fields) throws ParsingException { int offset = 0; int headerLen = Integer.parseInt(header.getAttribute("length")); NodeList nodes = header.getChildNodes(); for (int i = 0; i < nodes.getLength(); i++) { Node node = nodes.item(i); if (node.getNodeType() == Node.ELEMENT_NODE) { Pair<Field, Object> definition = s_readField((Element) node, offset); if (definition != null) { offset += definition.getKey().getType().getSize(); fields.put(definition.getKey(), definition.getValue()); } } } if (headerLen != offset) { throw new ParsingException( "Actual header length " + offset + " differs from given length " + headerLen + "!"); } return headerLen; } private static Pair<Field, Object> s_readField(Element field, int offset) { DataType dType = DataType.s_getDataType(field.getTagName()); // Will return blank if no name attribute String name = field.getAttribute("name"); Field f = new Field(name, dType, offset); // Now we have field, only need value String sVal = field.getTextContent(); Object val = DataTypeParser.s_parseDataType(dType, sVal); Pair<Field, Object> pair = new Pair<Field, Object>(f, val); return pair; } private static Msg s_createMsg(HashMap<Field, Object> values, int length, int headerLength, Msg.Direction dir) throws FieldException { Msg msg = new Msg(headerLength, new byte[length], length, dir); for (Entry<Field, Object> e : values.entrySet()) { Field f = e.getKey(); f.set(msg.getData(), e.getValue()); if (f.getName() != null && !f.getName().equals("")) { msg.addField(f); } } return msg; } public static void main(String[] args) throws Exception { // for local testing File f = new File(System.getProperty("user.home") + "/workspace/openhab/bundles/binding/org.openhab.binding.insteonplm/src/main/resources/msg_definitions.xml"); InputStream s = new FileInputStream(f); HashMap<String, Msg> msgs = XMLMessageReader.s_readMessageDefinitions(s); for (Msg msg : msgs.values()) { System.out.println(msg); } } }