]> git.basschouten.com Git - openhab-addons.git/blob
c23a18cc5c6aa5d31fbe33800a4e17dce27dac7f
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.insteon.internal.message;
14
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;
20
21 import javax.xml.parsers.DocumentBuilder;
22 import javax.xml.parsers.DocumentBuilderFactory;
23 import javax.xml.parsers.ParserConfigurationException;
24
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;
35
36 /**
37  * Reads the Msg definitions from an XML file
38  *
39  * @author Daniel Pfrommer - Initial contribution
40  * @author Rob Nielsen - Port to openHAB 2 insteon binding
41  */
42 @NonNullByDefault
43 @SuppressWarnings("null")
44 public class XMLMessageReader {
45     /**
46      * Reads the message definitions from an xml file
47      *
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
53      */
54     public static HashMap<String, Msg> readMessageDefinitions(InputStream input)
55             throws IOException, ParsingException, FieldException {
56         HashMap<String, Msg> messageMap = new HashMap<>();
57         try {
58             DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
59             DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
60             // Parse it!
61             Document doc = dBuilder.parse(input);
62             doc.getDocumentElement().normalize();
63
64             Node root = doc.getDocumentElement();
65
66             NodeList nodes = root.getChildNodes();
67
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());
74                     }
75                 }
76             }
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);
81         }
82         return messageMap;
83     }
84
85     private static Pair<String, Msg> readMessageDefinition(Element msg) throws FieldException, ParsingException {
86         int length = 0;
87         int hlength = 0;
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);
92
93         if (msg.hasAttribute("length")) {
94             length = Integer.parseInt(msg.getAttribute("length"));
95         }
96
97         NodeList nodes = msg.getChildNodes();
98
99         int offset = 0;
100
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);
106                     hlength = o;
107                     // Increment the offset by the header length
108                     offset += o;
109                 } else {
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();
114                 }
115             }
116         }
117         if (offset != length) {
118             throw new ParsingException(
119                     "Actual msg length " + offset + " differs from given msg length " + length + "!");
120         }
121         if (length == 0) {
122             length = offset;
123         }
124
125         return new Pair<>(name, createMsg(fieldMap, length, hlength, direction));
126     }
127
128     private static int readHeaderElement(Element header, LinkedHashMap<Field, Object> fields) throws ParsingException {
129         int offset = 0;
130         int headerLen = Integer.parseInt(header.getAttribute("length"));
131
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) {
136                 @Nullable
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());
141                 }
142             }
143         }
144         if (headerLen != offset) {
145             throw new ParsingException(
146                     "Actual header length " + offset + " differs from given length " + headerLen + "!");
147         }
148         return headerLen;
149     }
150
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);
160         return pair;
161     }
162
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("")) {
170                 msg.addField(f);
171             }
172         }
173         return msg;
174     }
175 }