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