]> git.basschouten.com Git - openhab-addons.git/blob
7a05a4768e8157e7222f61725335e069751f5770
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 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.device;
14
15 import java.io.IOException;
16 import java.io.InputStream;
17 import java.util.ArrayList;
18 import java.util.HashMap;
19 import java.util.List;
20 import java.util.Map;
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.Utils;
28 import org.openhab.binding.insteon.internal.utils.Utils.ParsingException;
29 import org.openhab.core.library.types.DecimalType;
30 import org.openhab.core.library.types.IncreaseDecreaseType;
31 import org.openhab.core.library.types.OnOffType;
32 import org.openhab.core.library.types.PercentType;
33 import org.openhab.core.types.Command;
34 import org.w3c.dom.DOMException;
35 import org.w3c.dom.Document;
36 import org.w3c.dom.Element;
37 import org.w3c.dom.NamedNodeMap;
38 import org.w3c.dom.Node;
39 import org.w3c.dom.NodeList;
40 import org.xml.sax.SAXException;
41
42 /**
43  * Class that loads the device feature templates from an xml stream
44  *
45  * @author Daniel Pfrommer - Initial contribution
46  * @author Rob Nielsen - Port to openHAB 2 insteon binding
47  */
48 @NonNullByDefault
49 public class FeatureTemplateLoader {
50     public static List<FeatureTemplate> readTemplates(InputStream input) throws IOException, ParsingException {
51         List<FeatureTemplate> features = new ArrayList<>();
52         try {
53             DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
54             // see https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html
55             dbFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
56             dbFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
57             dbFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
58             dbFactory.setXIncludeAware(false);
59             dbFactory.setExpandEntityReferences(false);
60             DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
61             // Parse it!
62             Document doc = dBuilder.parse(input);
63             doc.getDocumentElement().normalize();
64
65             Element root = doc.getDocumentElement();
66
67             NodeList nodes = root.getChildNodes();
68
69             for (int i = 0; i < nodes.getLength(); i++) {
70                 Node node = nodes.item(i);
71                 if (node.getNodeType() == Node.ELEMENT_NODE) {
72                     Element e = (Element) node;
73                     if ("feature".equals(e.getTagName())) {
74                         features.add(parseFeature(e));
75                     }
76                 }
77             }
78         } catch (SAXException e) {
79             throw new ParsingException("Failed to parse XML!", e);
80         } catch (ParserConfigurationException e) {
81             throw new ParsingException("Got parser config exception! ", e);
82         }
83         return features;
84     }
85
86     private static FeatureTemplate parseFeature(Element e) throws ParsingException {
87         String name = e.getAttribute("name");
88         boolean statusFeature = "true".equals(e.getAttribute("statusFeature"));
89         FeatureTemplate feature = new FeatureTemplate(name, statusFeature, e.getAttribute("timeout"));
90
91         NodeList nodes = e.getChildNodes();
92
93         for (int i = 0; i < nodes.getLength(); i++) {
94             Node node = nodes.item(i);
95             if (node.getNodeType() == Node.ELEMENT_NODE) {
96                 Element child = (Element) node;
97                 if ("message-handler".equals(child.getTagName())) {
98                     parseMessageHandler(child, feature);
99                 } else if ("command-handler".equals(child.getTagName())) {
100                     parseCommandHandler(child, feature);
101                 } else if ("message-dispatcher".equals(child.getTagName())) {
102                     parseMessageDispatcher(child, feature);
103                 } else if ("poll-handler".equals(child.getTagName())) {
104                     parsePollHandler(child, feature);
105                 }
106             }
107         }
108
109         return feature;
110     }
111
112     private static HandlerEntry makeHandlerEntry(Element e) throws ParsingException {
113         String handler = e.getTextContent();
114         if (handler == null) {
115             throw new ParsingException("Could not find Handler for: " + e.getTextContent());
116         }
117
118         NamedNodeMap attributes = e.getAttributes();
119         Map<String, String> params = new HashMap<>();
120         for (int i = 0; i < attributes.getLength(); i++) {
121             Node n = attributes.item(i);
122             params.put(n.getNodeName(), n.getNodeValue());
123         }
124         return new HandlerEntry(handler, params);
125     }
126
127     private static void parseMessageHandler(Element e, FeatureTemplate f) throws DOMException, ParsingException {
128         HandlerEntry he = makeHandlerEntry(e);
129         if ("true".equals(e.getAttribute("default"))) {
130             f.setDefaultMessageHandler(he);
131         } else {
132             String attr = e.getAttribute("cmd");
133             int command = (attr == null) ? 0 : Utils.from0xHexString(attr);
134             f.addMessageHandler(command, he);
135         }
136     }
137
138     private static void parseCommandHandler(Element e, FeatureTemplate f) throws ParsingException {
139         HandlerEntry he = makeHandlerEntry(e);
140         if ("true".equals(e.getAttribute("default"))) {
141             f.setDefaultCommandHandler(he);
142         } else {
143             Class<? extends Command> command = parseCommandClass(e.getAttribute("command"));
144             f.addCommandHandler(command, he);
145         }
146     }
147
148     private static void parseMessageDispatcher(Element e, FeatureTemplate f) throws DOMException, ParsingException {
149         HandlerEntry he = makeHandlerEntry(e);
150         f.setMessageDispatcher(he);
151     }
152
153     private static void parsePollHandler(Element e, FeatureTemplate f) throws ParsingException {
154         HandlerEntry he = makeHandlerEntry(e);
155         f.setPollHandler(he);
156     }
157
158     private static Class<? extends Command> parseCommandClass(String c) throws ParsingException {
159         if ("OnOffType".equals(c)) {
160             return OnOffType.class;
161         } else if ("PercentType".equals(c)) {
162             return PercentType.class;
163         } else if ("DecimalType".equals(c)) {
164             return DecimalType.class;
165         } else if ("IncreaseDecreaseType".equals(c)) {
166             return IncreaseDecreaseType.class;
167         } else {
168             throw new ParsingException("Unknown Command Type");
169         }
170     }
171 }