]> git.basschouten.com Git - openhab-addons.git/blob
6ec2b4ffe6d7f484e2f5fc1ee16c9a6d57005351
[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;
20 import java.util.Map.Entry;
21
22 import javax.xml.parsers.DocumentBuilder;
23 import javax.xml.parsers.DocumentBuilderFactory;
24 import javax.xml.parsers.ParserConfigurationException;
25
26 import org.eclipse.jdt.annotation.NonNullByDefault;
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 public class XMLMessageReader {
44     /**
45      * Reads the message definitions from an xml file
46      *
47      * @param input input stream from which to read
48      * @return what was read from file: the map between clear text string and Msg objects
49      * @throws IOException couldn't read file etc
50      * @throws ParsingException something wrong with the file format
51      * @throws FieldException something wrong with the field definition
52      */
53     public static Map<String, Msg> readMessageDefinitions(InputStream input)
54             throws IOException, ParsingException, FieldException {
55         Map<String, Msg> messageMap = new HashMap<>();
56         try {
57             DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
58             DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
59             // Parse it!
60             Document doc = dBuilder.parse(input);
61             doc.getDocumentElement().normalize();
62
63             Node root = doc.getDocumentElement();
64
65             NodeList nodes = root.getChildNodes();
66
67             for (int i = 0; i < nodes.getLength(); i++) {
68                 Node node = nodes.item(i);
69                 if (node.getNodeType() == Node.ELEMENT_NODE) {
70                     if (node.getNodeName().equals("msg")) {
71                         Pair<String, Msg> msgDef = readMessageDefinition((Element) node);
72                         messageMap.put(msgDef.getKey(), msgDef.getValue());
73                     }
74                 }
75             }
76         } catch (SAXException e) {
77             throw new ParsingException("Failed to parse XML!", e);
78         } catch (ParserConfigurationException e) {
79             throw new ParsingException("Got parser config exception! ", e);
80         }
81         return messageMap;
82     }
83
84     private static Pair<String, Msg> readMessageDefinition(Element msg) throws FieldException, ParsingException {
85         int length = 0;
86         int hlength = 0;
87         LinkedHashMap<Field, Object> fieldMap = new LinkedHashMap<>();
88         String dir = msg.getAttribute("direction");
89         String name = msg.getAttribute("name");
90         Msg.Direction direction = Msg.Direction.getDirectionFromString(dir);
91
92         if (msg.hasAttribute("length")) {
93             length = Integer.parseInt(msg.getAttribute("length"));
94         }
95
96         NodeList nodes = msg.getChildNodes();
97
98         int offset = 0;
99
100         for (int i = 0; i < nodes.getLength(); i++) {
101             Node node = nodes.item(i);
102             if (node.getNodeType() == Node.ELEMENT_NODE) {
103                 if (node.getNodeName().equals("header")) {
104                     int o = readHeaderElement((Element) node, fieldMap);
105                     hlength = o;
106                     // Increment the offset by the header length
107                     offset += o;
108                 } else {
109                     Pair<Field, Object> field = readField((Element) node, offset);
110                     fieldMap.put(field.getKey(), field.getValue());
111                     // Increment the offset
112                     offset += field.getKey().getType().getSize();
113                 }
114             }
115         }
116         if (offset != length) {
117             throw new ParsingException(
118                     "Actual msg length " + offset + " differs from given msg length " + length + "!");
119         }
120         if (length == 0) {
121             length = offset;
122         }
123
124         return new Pair<>(name, createMsg(fieldMap, length, hlength, direction));
125     }
126
127     private static int readHeaderElement(Element header, LinkedHashMap<Field, Object> fields) throws ParsingException {
128         int offset = 0;
129         int headerLen = Integer.parseInt(header.getAttribute("length"));
130
131         NodeList nodes = header.getChildNodes();
132         for (int i = 0; i < nodes.getLength(); i++) {
133             Node node = nodes.item(i);
134             if (node.getNodeType() == Node.ELEMENT_NODE) {
135                 Pair<Field, Object> definition = readField((Element) node, offset);
136                 offset += definition.getKey().getType().getSize();
137                 fields.put(definition.getKey(), definition.getValue());
138             }
139         }
140         if (headerLen != offset) {
141             throw new ParsingException(
142                     "Actual header length " + offset + " differs from given length " + headerLen + "!");
143         }
144         return headerLen;
145     }
146
147     private static Pair<Field, Object> readField(Element field, int offset) {
148         DataType dType = DataType.getDataType(field.getTagName());
149         // Will return blank if no name attribute
150         String name = field.getAttribute("name");
151         Field f = new Field(name, dType, offset);
152         // Now we have field, only need value
153         String sVal = field.getTextContent();
154         Object val = DataTypeParser.parseDataType(dType, sVal);
155         Pair<Field, Object> pair = new Pair<>(f, val);
156         return pair;
157     }
158
159     private static Msg createMsg(HashMap<Field, Object> values, int length, int headerLength, Msg.Direction dir)
160             throws FieldException {
161         Msg msg = new Msg(headerLength, new byte[length], length, dir);
162         for (Entry<Field, Object> e : values.entrySet()) {
163             Field f = e.getKey();
164             byte[] data = msg.getData();
165             if (data != null) {
166                 f.set(data, e.getValue());
167             } else {
168                 throw new FieldException("data is null");
169             }
170             if (!f.getName().equals("")) {
171                 msg.addField(f);
172             }
173         }
174         return msg;
175     }
176 }