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.homematic.internal.communicator.message;
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;
24 import java.util.Map.Entry;
27 * A XML-RPC request for sending data to the Homematic server.
29 * @author Gerhard Riegler - Initial contribution
31 public class XmlRpcRequest implements RpcRequest<String> {
38 private String methodName;
39 private List<Object> parms;
40 private StringBuilder sb;
42 public static final SimpleDateFormat XML_RPC_DATEFORMAT = new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss");
44 public XmlRpcRequest(String methodName) {
45 this(methodName, TYPE.REQUEST);
48 public XmlRpcRequest(String methodName, TYPE type) {
49 this.methodName = methodName;
51 parms = new ArrayList<>();
55 public void addArg(Object parameter) {
60 public String createMessage() {
65 public String getMethodName() {
70 public String toString() {
71 sb = new StringBuilder();
74 attr("version", "1.0");
75 attr("encoding", "ISO-8859-1");
78 if (type == TYPE.REQUEST) {
79 sb.append("<methodCall>");
80 tag("methodName", methodName);
82 sb.append("<methodResponse>");
86 sb.append("<params>");
87 for (Object parameter : parms) {
88 sb.append("<param><value>");
89 generateValue(parameter);
90 sb.append("</value></param>");
92 sb.append("</params>");
94 if (type == TYPE.REQUEST) {
95 sb.append("</methodCall>");
97 sb.append("</methodResponse>");
103 * Generates a XML attribute.
105 private void attr(String name, String value) {
106 sb.append(" ").append(name).append("=\"").append(value).append("\"");
110 * Generates a XML tag.
112 private void tag(String name, String value) {
113 sb.append("<").append(name).append(">").append(value).append("</").append(name).append(">");
117 * Generates a value tag based on the type of the value.
119 private void generateValue(Object value) {
121 tag("string", "void");
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 synchronized (XML_RPC_DATEFORMAT) {
139 tag("dateTime.iso8601", XML_RPC_DATEFORMAT.format(((Date) value)));
141 } else if (value instanceof Calendar calendar) {
142 generateValue(calendar.getTime());
143 } else if (value instanceof byte[] bytes) {
144 tag("base64", Base64.getEncoder().encodeToString(bytes));
145 } else if (clazz.isArray() || value instanceof List) {
146 sb.append("<array><data>");
148 Object[] array = null;
149 if (value instanceof List list) {
150 array = list.toArray();
152 array = (Object[]) value;
154 for (Object arrayObject : array) {
155 sb.append("<value>");
156 generateValue(arrayObject);
157 sb.append("</value>");
160 sb.append("</data></array>");
161 } else if (value instanceof Map) {
162 sb.append("<struct>");
164 for (Entry<?, ?> entry : ((Map<?, ?>) value).entrySet()) {
165 sb.append("<member>");
166 sb.append("<name>").append(entry.getKey()).append("</name>");
167 sb.append("<value>");
168 generateValue(entry.getValue());
169 sb.append("</value>");
170 sb.append("</member>");
173 sb.append("</struct>");
175 throw new RuntimeException("Unsupported XML-RPC Type: " + value.getClass());
180 private StringBuilder escapeXml(String inValue) {
181 StringBuilder outValue = new StringBuilder(inValue.length());
182 for (int i = 0; i < inValue.length(); i++) {
183 switch (inValue.charAt(i)) {
185 outValue.append("<");
188 outValue.append(">");
191 outValue.append("&");
194 outValue.append("&apost;");
197 outValue.append(""");
200 outValue.append(inValue.charAt(i));