2 * Copyright (c) 2010-2020 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.insteon.internal.message;
15 import java.io.IOException;
16 import java.io.InputStream;
17 import java.util.HashMap;
18 import java.util.LinkedHashMap;
19 import java.util.Map.Entry;
21 import javax.xml.parsers.DocumentBuilder;
22 import javax.xml.parsers.DocumentBuilderFactory;
23 import javax.xml.parsers.ParserConfigurationException;
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.openhab.binding.insteon.internal.utils.Pair;
28 import org.openhab.binding.insteon.internal.utils.Utils.DataTypeParser;
29 import org.openhab.binding.insteon.internal.utils.Utils.ParsingException;
30 import org.w3c.dom.Document;
31 import org.w3c.dom.Element;
32 import org.w3c.dom.Node;
33 import org.w3c.dom.NodeList;
34 import org.xml.sax.SAXException;
37 * Reads the Msg definitions from an XML file
39 * @author Daniel Pfrommer - Initial contribution
40 * @author Rob Nielsen - Port to openHAB 2 insteon binding
43 @SuppressWarnings("null")
44 public class XMLMessageReader {
46 * Reads the message definitions from an xml file
48 * @param input input stream from which to read
49 * @return what was read from file: the map between clear text string and Msg objects
50 * @throws IOException couldn't read file etc
51 * @throws ParsingException something wrong with the file format
52 * @throws FieldException something wrong with the field definition
54 public static HashMap<String, Msg> readMessageDefinitions(InputStream input)
55 throws IOException, ParsingException, FieldException {
56 HashMap<String, Msg> messageMap = new HashMap<>();
58 DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
59 DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
61 Document doc = dBuilder.parse(input);
62 doc.getDocumentElement().normalize();
64 Node root = doc.getDocumentElement();
66 NodeList nodes = root.getChildNodes();
68 for (int i = 0; i < nodes.getLength(); i++) {
69 Node node = nodes.item(i);
70 if (node.getNodeType() == Node.ELEMENT_NODE) {
71 if (node.getNodeName().equals("msg")) {
72 Pair<String, Msg> msgDef = readMessageDefinition((Element) node);
73 messageMap.put(msgDef.getKey(), msgDef.getValue());
77 } catch (SAXException e) {
78 throw new ParsingException("Failed to parse XML!", e);
79 } catch (ParserConfigurationException e) {
80 throw new ParsingException("Got parser config exception! ", e);
85 private static Pair<String, Msg> readMessageDefinition(Element msg) throws FieldException, ParsingException {
88 LinkedHashMap<Field, Object> fieldMap = new LinkedHashMap<>();
89 String dir = msg.getAttribute("direction");
90 String name = msg.getAttribute("name");
91 Msg.Direction direction = Msg.Direction.getDirectionFromString(dir);
93 if (msg.hasAttribute("length")) {
94 length = Integer.parseInt(msg.getAttribute("length"));
97 NodeList nodes = msg.getChildNodes();
101 for (int i = 0; i < nodes.getLength(); i++) {
102 Node node = nodes.item(i);
103 if (node.getNodeType() == Node.ELEMENT_NODE) {
104 if (node.getNodeName().equals("header")) {
105 int o = readHeaderElement((Element) node, fieldMap);
107 // Increment the offset by the header length
110 Pair<Field, Object> field = readField((Element) node, offset);
111 fieldMap.put(field.getKey(), field.getValue());
112 // Increment the offset
113 offset += field.getKey().getType().getSize();
117 if (offset != length) {
118 throw new ParsingException(
119 "Actual msg length " + offset + " differs from given msg length " + length + "!");
125 return new Pair<>(name, createMsg(fieldMap, length, hlength, direction));
128 private static int readHeaderElement(Element header, LinkedHashMap<Field, Object> fields) throws ParsingException {
130 int headerLen = Integer.parseInt(header.getAttribute("length"));
132 NodeList nodes = header.getChildNodes();
133 for (int i = 0; i < nodes.getLength(); i++) {
134 Node node = nodes.item(i);
135 if (node.getNodeType() == Node.ELEMENT_NODE) {
137 Pair<Field, Object> definition = readField((Element) node, offset);
138 if (definition != null) {
139 offset += definition.getKey().getType().getSize();
140 fields.put(definition.getKey(), definition.getValue());
144 if (headerLen != offset) {
145 throw new ParsingException(
146 "Actual header length " + offset + " differs from given length " + headerLen + "!");
151 private static Pair<Field, Object> readField(Element field, int offset) {
152 DataType dType = DataType.getDataType(field.getTagName());
153 // Will return blank if no name attribute
154 String name = field.getAttribute("name");
155 Field f = new Field(name, dType, offset);
156 // Now we have field, only need value
157 String sVal = field.getTextContent();
158 Object val = DataTypeParser.parseDataType(dType, sVal);
159 Pair<Field, Object> pair = new Pair<>(f, val);
163 private static Msg createMsg(HashMap<Field, Object> values, int length, int headerLength, Msg.Direction dir)
164 throws FieldException {
165 Msg msg = new Msg(headerLength, new byte[length], length, dir);
166 for (Entry<Field, Object> e : values.entrySet()) {
167 Field f = e.getKey();
168 f.set(msg.getData(), e.getValue());
169 if (f.getName() != null && !f.getName().equals("")) {