2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.fsinternetradio.internal.radio;
15 import java.io.IOException;
16 import java.io.StringReader;
18 import javax.xml.parsers.DocumentBuilder;
19 import javax.xml.parsers.DocumentBuilderFactory;
20 import javax.xml.parsers.ParserConfigurationException;
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;
33 * This class hold the result of a request read from the radio. Upon a request the radio returns a XML document like
38 * <fsapiResponse> <status>FS_OK</status> <value><u8>1</u8></value> </fsapiResponse>
42 * This class parses this XML data and provides functions for reading and casting typical fields.
44 * @author Rainer Ostendorf
45 * @author Patrick Koenemann
48 public class FrontierSiliconRadioApiResult {
51 * XML structure holding the parsed response
53 final Document xmlDoc;
55 private final Logger logger = LoggerFactory.getLogger(FrontierSiliconRadioApiResult.class);
58 * Create result object from XML that was received from the radio.
60 * @param requestResultString
61 * The XML string received from the radio.
62 * @throws IOException in case the XML returned by the radio is invalid.
64 public FrontierSiliconRadioApiResult(String requestResultString) throws IOException {
67 xml = getXmlDocFromString(requestResultString);
68 } catch (Exception e) {
69 logger.trace("converting to XML failed: '{}' with {}: {}", requestResultString, e.getClass().getName(),
71 logger.debug("converting to XML failed with {}: {}", e.getClass().getName(), e.getMessage());
72 if (e instanceof IOException exception) {
75 throw new IOException(e);
81 * Extract the field "status" from the result and return it
83 * @return result field as string.
85 private String getStatus() {
86 final Element fsApiResult = (Element) xmlDoc.getElementsByTagName("fsapiResponse").item(0);
87 final Element statusNode = (Element) fsApiResult.getElementsByTagName("status").item(0);
89 final String status = getCharacterDataFromElement(statusNode);
90 logger.trace("status is: {}", status);
96 * checks if the responses status code was "FS_OK"
98 * @return true if status is "FS_OK", false else
100 public boolean isStatusOk() {
101 return ("FS_OK").equals(getStatus());
105 * read the <value><u8> field as boolean
107 * @return value.u8 field as bool
109 public boolean getValueU8AsBoolean() {
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);
115 final String value = getCharacterDataFromElement(u8Node);
116 logger.trace("value is: {}", value);
118 return "1".equals(value);
119 } catch (Exception e) {
120 logger.error("getting Value.U8 failed with {}: {}", e.getClass().getName(), e.getMessage());
126 * read the <value><u8> field as int
128 * @return value.u8 field as int
130 public int getValueU8AsInt() {
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);
136 final String value = getCharacterDataFromElement(u8Node);
137 logger.trace("value is: {}", value);
139 return Integer.parseInt(value);
140 } catch (Exception e) {
141 logger.error("getting Value.U8 failed with {}: {}", e.getClass().getName(), e.getMessage());
147 * read the <value><u32> field as int
149 * @return value.u32 field as int
151 public int getValueU32AsInt() {
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);
157 final String value = getCharacterDataFromElement(u32Node);
158 logger.trace("value is: {}", value);
160 return Integer.parseInt(value);
161 } catch (Exception e) {
162 logger.error("getting Value.U32 failed with {}: {}", e.getClass().getName(), e.getMessage());
168 * read the <value><c8_array> field as String
170 * @return value.c8_array field as String
172 public String getValueC8ArrayAsString() {
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);
178 final String value = getCharacterDataFromElement(c8Array);
179 logger.trace("value is: {}", value);
182 } catch (Exception e) {
183 logger.error("getting Value.c8array failed with {}: {}", e.getClass().getName(), e.getMessage());
189 * read the <sessionId> field as String
191 * @return value of sessionId field
193 public String getSessionId() {
194 final NodeList sessionIdTagList = xmlDoc.getElementsByTagName("sessionId");
195 return getCharacterDataFromElement((Element) sessionIdTagList.item(0));
199 * converts the string we got from the radio to a parsable XML document
202 * the XML string read from the radio
203 * @return the parsed XML document
204 * @throws ParserConfigurationException
205 * @throws SAXException
206 * @throws IOException
208 private Document getXmlDocFromString(String xmlString)
209 throws ParserConfigurationException, SAXException, IOException {
210 final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
211 // see https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html
212 factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
213 factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
214 factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
215 factory.setXIncludeAware(false);
216 factory.setExpandEntityReferences(false);
217 final DocumentBuilder builder = factory.newDocumentBuilder();
218 return builder.parse(new InputSource(new StringReader(xmlString)));
222 * convert the value of a given XML element to a string for further processing
226 * @return the elements value converted to string
228 private static String getCharacterDataFromElement(Element e) {
229 final Node child = e.getFirstChild();
230 if (child instanceof CharacterData cd) {