]> git.basschouten.com Git - openhab-addons.git/blob
d30873df3b07a77d6a1d7360f700fba36475f483
[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.math.BigDecimal;
16 import java.math.RoundingMode;
17 import java.text.SimpleDateFormat;
18 import java.util.ArrayList;
19 import java.util.Base64;
20 import java.util.Calendar;
21 import java.util.Date;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Map.Entry;
25
26 /**
27  * A XML-RPC request for sending data to the Homematic server.
28  *
29  * @author Gerhard Riegler - Initial contribution
30  */
31 public class XmlRpcRequest implements RpcRequest<String> {
32
33     public enum TYPE {
34         REQUEST,
35         RESPONSE
36     }
37
38     private String methodName;
39     private List<Object> parms;
40     private StringBuilder sb;
41     private TYPE type;
42     public static SimpleDateFormat xmlRpcDateFormat = new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss");
43
44     public XmlRpcRequest(String methodName) {
45         this(methodName, TYPE.REQUEST);
46     }
47
48     public XmlRpcRequest(String methodName, TYPE type) {
49         this.methodName = methodName;
50         this.type = type;
51         parms = new ArrayList<>();
52     }
53
54     @Override
55     public void addArg(Object parameter) {
56         parms.add(parameter);
57     }
58
59     @Override
60     public String createMessage() {
61         return toString();
62     }
63
64     @Override
65     public String getMethodName() {
66         return methodName;
67     }
68
69     @Override
70     public String toString() {
71         sb = new StringBuilder();
72
73         sb.append("<?xml");
74         attr("version", "1.0");
75         attr("encoding", "ISO-8859-1");
76         sb.append("?>\n");
77
78         if (type == TYPE.REQUEST) {
79             sb.append("<methodCall>");
80             tag("methodName", methodName);
81         } else {
82             sb.append("<methodResponse>");
83         }
84
85         sb.append("\n");
86         sb.append("<params>");
87         for (Object parameter : parms) {
88             sb.append("<param><value>");
89             generateValue(parameter);
90             sb.append("</value></param>");
91         }
92         sb.append("</params>");
93
94         if (type == TYPE.REQUEST) {
95             sb.append("</methodCall>");
96         } else {
97             sb.append("</methodResponse>");
98         }
99         return sb.toString();
100     }
101
102     /**
103      * Generates a XML attribute.
104      */
105     private void attr(String name, String value) {
106         sb.append(" ").append(name).append("=\"").append(value).append("\"");
107     }
108
109     /**
110      * Generates a XML tag.
111      */
112     private void tag(String name, String value) {
113         sb.append("<").append(name).append(">").append(value).append("</").append(name).append(">");
114     }
115
116     /**
117      * Generates a value tag based on the type of the value.
118      */
119     private void generateValue(Object value) {
120         if (value == null) {
121             tag("string", "void");
122         } else {
123             Class<?> clazz = value.getClass();
124             if (clazz == String.class || clazz == Character.class) {
125                 sb.append(escapeXml(value.toString()));
126             } else if (clazz == Long.class || clazz == Integer.class || clazz == Short.class || clazz == Byte.class) {
127                 tag("int", value.toString());
128             } else if (clazz == Double.class) {
129                 tag("double", String.valueOf(((Double) value).doubleValue()));
130             } else if (clazz == Float.class) {
131                 BigDecimal bd = new BigDecimal((Float) value);
132                 generateValue(bd.setScale(6, RoundingMode.HALF_DOWN).doubleValue());
133             } else if (clazz == BigDecimal.class) {
134                 generateValue(((BigDecimal) value).setScale(6, RoundingMode.HALF_DOWN).doubleValue());
135             } else if (clazz == Boolean.class) {
136                 tag("boolean", ((Boolean) value).booleanValue() ? "1" : "0");
137             } else if (clazz == Date.class) {
138                 tag("dateTime.iso8601", xmlRpcDateFormat.format(((Date) value)));
139             } else if (value instanceof Calendar) {
140                 generateValue(((Calendar) value).getTime());
141             } else if (value instanceof byte[]) {
142                 tag("base64", Base64.getEncoder().encodeToString((byte[]) value));
143             } else if (clazz.isArray() || value instanceof List) {
144                 sb.append("<array><data>");
145
146                 Object[] array = null;
147                 if (value instanceof List) {
148                     array = ((List<?>) value).toArray();
149                 } else {
150                     array = (Object[]) value;
151                 }
152                 for (Object arrayObject : array) {
153                     sb.append("<value>");
154                     generateValue(arrayObject);
155                     sb.append("</value>");
156                 }
157
158                 sb.append("</data></array>");
159             } else if (value instanceof Map) {
160                 sb.append("<struct>");
161
162                 for (Entry<?, ?> entry : ((Map<?, ?>) value).entrySet()) {
163                     sb.append("<member>");
164                     sb.append("<name>").append(entry.getKey()).append("</name>");
165                     sb.append("<value>");
166                     generateValue(entry.getValue());
167                     sb.append("</value>");
168                     sb.append("</member>");
169                 }
170
171                 sb.append("</struct>");
172             } else {
173                 throw new RuntimeException("Unsupported XML-RPC Type: " + value.getClass());
174             }
175         }
176     }
177
178     private StringBuilder escapeXml(String inValue) {
179         StringBuilder outValue = new StringBuilder(inValue.length());
180         for (int i = 0; i < inValue.length(); i++) {
181             switch (inValue.charAt(i)) {
182                 case '<':
183                     outValue.append("&lt;");
184                     break;
185                 case '>':
186                     outValue.append("&gt;");
187                     break;
188                 case '&':
189                     outValue.append("&amp;");
190                     break;
191                 case '\'':
192                     outValue.append("&apost;");
193                     break;
194                 case '"':
195                     outValue.append("&quot;");
196                     break;
197                 default:
198                     outValue.append(inValue.charAt(i));
199             }
200         }
201         return outValue;
202     }
203 }