]> git.basschouten.com Git - openhab-addons.git/blob
464318ea250600b867d15d93ca4f475b48d8afbc
[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.fsinternetradio.internal.radio;
14
15 import java.io.IOException;
16 import java.io.StringReader;
17
18 import javax.xml.parsers.DocumentBuilder;
19 import javax.xml.parsers.DocumentBuilderFactory;
20 import javax.xml.parsers.ParserConfigurationException;
21
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
24 import org.w3c.dom.CharacterData;
25 import org.w3c.dom.Document;
26 import org.w3c.dom.Element;
27 import org.w3c.dom.Node;
28 import org.w3c.dom.NodeList;
29 import org.xml.sax.InputSource;
30 import org.xml.sax.SAXException;
31
32 /**
33  * This class hold the result of a request read from the radio. Upon a request the radio returns a XML document like
34  * this:
35  *
36  * <pre>
37  * {@code
38  * <xmp>
39  *   <fsapiResponse> <status>FS_OK</status> <value><u8>1</u8></value> </fsapiResponse>
40  * </xmp>
41  * }
42  * </pre>
43  *
44  * This class parses this XML data and provides functions for reading and casting typical fields.
45  *
46  * @author Rainer Ostendorf
47  * @author Patrick Koenemann
48  *
49  */
50 public class FrontierSiliconRadioApiResult {
51
52     /**
53      * XML structure holding the parsed response
54      */
55     final Document xmlDoc;
56
57     private final Logger logger = LoggerFactory.getLogger(FrontierSiliconRadioApiResult.class);
58
59     /**
60      * Create result object from XML that was received from the radio.
61      *
62      * @param requestResultString
63      *            The XML string received from the radio.
64      * @throws IOException in case the XML returned by the radio is invalid.
65      */
66     public FrontierSiliconRadioApiResult(String requestResultString) throws IOException {
67         Document xml = null;
68         try {
69             xml = getXmlDocFromString(requestResultString);
70         } catch (Exception e) {
71             logger.trace("converting to XML failed: '{}' with {}: {}", requestResultString, e.getClass().getName(),
72                     e.getMessage());
73             logger.debug("converting to XML failed with {}: {}", e.getClass().getName(), e.getMessage());
74             if (e instanceof IOException exception) {
75                 throw exception;
76             }
77             throw new IOException(e);
78         }
79         xmlDoc = xml;
80     }
81
82     /**
83      * Extract the field "status" from the result and return it
84      *
85      * @return result field as string.
86      */
87     private String getStatus() {
88         final Element fsApiResult = (Element) xmlDoc.getElementsByTagName("fsapiResponse").item(0);
89         final Element statusNode = (Element) fsApiResult.getElementsByTagName("status").item(0);
90
91         final String status = getCharacterDataFromElement(statusNode);
92         logger.trace("status is: {}", status);
93
94         return status;
95     }
96
97     /**
98      * checks if the responses status code was "FS_OK"
99      *
100      * @return true if status is "FS_OK", false else
101      */
102     public boolean isStatusOk() {
103         return ("FS_OK").equals(getStatus());
104     }
105
106     /**
107      * read the &lt;value&gt;&lt;u8&gt; field as boolean
108      *
109      * @return value.u8 field as bool
110      */
111     public boolean getValueU8AsBoolean() {
112         try {
113             final Element fsApiResult = (Element) xmlDoc.getElementsByTagName("fsapiResponse").item(0);
114             final Element valueNode = (Element) fsApiResult.getElementsByTagName("value").item(0);
115             final Element u8Node = (Element) valueNode.getElementsByTagName("u8").item(0);
116
117             final String value = getCharacterDataFromElement(u8Node);
118             logger.trace("value is: {}", value);
119
120             return "1".equals(value);
121         } catch (Exception e) {
122             logger.error("getting Value.U8 failed with {}: {}", e.getClass().getName(), e.getMessage());
123             return false;
124         }
125     }
126
127     /**
128      * read the &lt;value&gt;&lt;u8&gt; field as int
129      *
130      * @return value.u8 field as int
131      */
132     public int getValueU8AsInt() {
133         try {
134             final Element fsApiResult = (Element) xmlDoc.getElementsByTagName("fsapiResponse").item(0);
135             final Element valueNode = (Element) fsApiResult.getElementsByTagName("value").item(0);
136             final Element u8Node = (Element) valueNode.getElementsByTagName("u8").item(0);
137
138             final String value = getCharacterDataFromElement(u8Node);
139             logger.trace("value is: {}", value);
140
141             return Integer.parseInt(value);
142         } catch (Exception e) {
143             logger.error("getting Value.U8 failed with {}: {}", e.getClass().getName(), e.getMessage());
144             return 0;
145         }
146     }
147
148     /**
149      * read the &lt;value&gt;&lt;u32&gt; field as int
150      *
151      * @return value.u32 field as int
152      */
153     public int getValueU32AsInt() {
154         try {
155             final Element fsApiResult = (Element) xmlDoc.getElementsByTagName("fsapiResponse").item(0);
156             final Element valueNode = (Element) fsApiResult.getElementsByTagName("value").item(0);
157             final Element u32Node = (Element) valueNode.getElementsByTagName("u32").item(0);
158
159             final String value = getCharacterDataFromElement(u32Node);
160             logger.trace("value is: {}", value);
161
162             return Integer.parseInt(value);
163         } catch (Exception e) {
164             logger.error("getting Value.U32 failed with {}: {}", e.getClass().getName(), e.getMessage());
165             return 0;
166         }
167     }
168
169     /**
170      * read the &lt;value&gt;&lt;c8_array&gt; field as String
171      *
172      * @return value.c8_array field as String
173      */
174     public String getValueC8ArrayAsString() {
175         try {
176             final Element fsApiResult = (Element) xmlDoc.getElementsByTagName("fsapiResponse").item(0);
177             final Element valueNode = (Element) fsApiResult.getElementsByTagName("value").item(0);
178             final Element c8Array = (Element) valueNode.getElementsByTagName("c8_array").item(0);
179
180             final String value = getCharacterDataFromElement(c8Array);
181             logger.trace("value is: {}", value);
182
183             return value;
184         } catch (Exception e) {
185             logger.error("getting Value.c8array failed with {}: {}", e.getClass().getName(), e.getMessage());
186             return "";
187         }
188     }
189
190     /**
191      * read the &lt;sessionId&gt; field as String
192      *
193      * @return value of sessionId field
194      */
195     public String getSessionId() {
196         final NodeList sessionIdTagList = xmlDoc.getElementsByTagName("sessionId");
197         return getCharacterDataFromElement((Element) sessionIdTagList.item(0));
198     }
199
200     /**
201      * converts the string we got from the radio to a parsable XML document
202      *
203      * @param xmlString
204      *            the XML string read from the radio
205      * @return the parsed XML document
206      * @throws ParserConfigurationException
207      * @throws SAXException
208      * @throws IOException
209      */
210     private Document getXmlDocFromString(String xmlString)
211             throws ParserConfigurationException, SAXException, IOException {
212         final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
213         // see https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html
214         factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
215         factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
216         factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
217         factory.setXIncludeAware(false);
218         factory.setExpandEntityReferences(false);
219         final DocumentBuilder builder = factory.newDocumentBuilder();
220         return builder.parse(new InputSource(new StringReader(xmlString)));
221     }
222
223     /**
224      * convert the value of a given XML element to a string for further processing
225      *
226      * @param e
227      *            XML Element
228      * @return the elements value converted to string
229      */
230     private static String getCharacterDataFromElement(Element e) {
231         final Node child = e.getFirstChild();
232         if (child instanceof CharacterData cd) {
233             return cd.getData();
234         }
235         return "";
236     }
237 }