]> git.basschouten.com Git - openhab-addons.git/blob
a993efcf7f683b45dd9aa897ee09a446ac5f2416
[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.homematic.internal.communicator.message;
14
15 import java.io.IOException;
16 import java.io.InputStream;
17 import java.nio.charset.Charset;
18 import java.text.ParseException;
19 import java.util.ArrayList;
20 import java.util.Base64;
21 import java.util.HashMap;
22 import java.util.LinkedList;
23 import java.util.List;
24 import java.util.Map;
25
26 import javax.xml.parsers.ParserConfigurationException;
27 import javax.xml.parsers.SAXParser;
28 import javax.xml.parsers.SAXParserFactory;
29
30 import org.xml.sax.Attributes;
31 import org.xml.sax.InputSource;
32 import org.xml.sax.SAXException;
33 import org.xml.sax.helpers.DefaultHandler;
34
35 /**
36  * Decodes a XML-RPC message from the Homematic server.
37  *
38  * @author Gerhard Riegler - Initial contribution
39  */
40 public class XmlRpcResponse implements RpcResponse {
41     private String methodName;
42     private Object[] responseData;
43
44     /**
45      * Decodes a XML-RPC message from the given InputStream.
46      */
47     public XmlRpcResponse(InputStream is, Charset encoding)
48             throws SAXException, ParserConfigurationException, IOException {
49         SAXParserFactory factory = SAXParserFactory.newInstance();
50         SAXParser saxParser = factory.newSAXParser();
51         factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
52         saxParser.getXMLReader().setFeature("http://xml.org/sax/features/external-general-entities", false);
53         factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
54         InputSource inputSource = new InputSource(is);
55         inputSource.setEncoding(encoding.name());
56         saxParser.parse(inputSource, new XmlRpcHandler());
57     }
58
59     @Override
60     public Object[] getResponseData() {
61         return responseData;
62     }
63
64     @Override
65     public String getMethodName() {
66         return methodName;
67     }
68
69     @Override
70     public String toString() {
71         return RpcUtils.dumpRpcMessage(methodName, responseData);
72     }
73
74     /**
75      * SAX parser implementation to decode XML-RPC.
76      *
77      * @author Gerhard Riegler
78      */
79     private class XmlRpcHandler extends DefaultHandler {
80         private List<Object> result = new ArrayList<>();
81         private LinkedList<List<Object>> currentDataObject = new LinkedList<>();
82         private StringBuilder tagValue;
83         private boolean isValueTag;
84
85         @Override
86         public void startDocument() throws SAXException {
87             currentDataObject.addLast(new ArrayList<>());
88         }
89
90         @Override
91         public void endDocument() throws SAXException {
92             result.addAll(currentDataObject.removeLast());
93             responseData = result.toArray();
94         }
95
96         @Override
97         public void startElement(String uri, String localName, String qName, Attributes attributes)
98                 throws SAXException {
99             String tag = qName.toLowerCase();
100             if ("array".equals(tag) || "struct".equals(tag)) {
101                 currentDataObject.addLast(new ArrayList<>());
102             }
103             isValueTag = "value".equals(tag);
104             tagValue = new StringBuilder();
105         }
106
107         @Override
108         public void endElement(String uri, String localName, String qName) throws SAXException {
109             String currentTag = qName.toLowerCase();
110             String currentValue = tagValue.toString();
111             List<Object> data = currentDataObject.peekLast();
112
113             switch (currentTag) {
114                 case "boolean":
115                     data.add("1".equals(currentValue) ? Boolean.TRUE : Boolean.FALSE);
116                     break;
117                 case "int":
118                 case "i4":
119                     data.add(Integer.valueOf(currentValue));
120                     break;
121                 case "double":
122                     data.add(Double.valueOf(currentValue));
123                     break;
124                 case "string":
125                 case "name":
126                     data.add(currentValue);
127                     break;
128                 case "value":
129                     if (isValueTag) {
130                         data.add(currentValue);
131                         isValueTag = false;
132                     }
133                     break;
134                 case "array":
135                     List<Object> arrayData = currentDataObject.removeLast();
136                     currentDataObject.peekLast().add(arrayData.toArray());
137                     break;
138                 case "struct":
139                     List<Object> mapData = currentDataObject.removeLast();
140                     Map<Object, Object> resultMap = new HashMap<>();
141
142                     for (int i = 0; i < mapData.size(); i += 2) {
143                         resultMap.put(mapData.get(i), mapData.get(i + 1));
144                     }
145                     currentDataObject.peekLast().add(resultMap);
146                     break;
147                 case "base64":
148                     data.add(Base64.getDecoder().decode(currentValue));
149                     break;
150                 case "datetime.iso8601":
151                     try {
152                         data.add(XmlRpcRequest.XML_RPC_DATEFORMAT.parse(currentValue));
153                     } catch (ParseException ex) {
154                         throw new SAXException(ex.getMessage(), ex);
155                     }
156                     break;
157                 case "methodname":
158                     methodName = currentValue;
159                     break;
160                 case "params":
161                 case "param":
162                 case "methodcall":
163                 case "methodresponse":
164                 case "member":
165                 case "data":
166                 case "fault":
167                     break;
168                 default:
169                     throw new SAXException("Unknown XML-RPC tag: " + currentTag);
170             }
171         }
172
173         @Override
174         public void characters(char[] ch, int start, int length) throws SAXException {
175             tagValue.append(new String(ch, start, length));
176         }
177     }
178 }