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