2 * Copyright (c) 2010-2021 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.heos.internal.json;
15 import java.io.UnsupportedEncodingException;
16 import java.net.URLDecoder;
17 import java.nio.charset.StandardCharsets;
18 import java.util.Arrays;
19 import java.util.Collections;
21 import java.util.Objects;
22 import java.util.stream.Collectors;
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.openhab.binding.heos.internal.json.dto.HeosCommandTuple;
27 import org.openhab.binding.heos.internal.json.dto.HeosEvent;
28 import org.openhab.binding.heos.internal.json.dto.HeosEventObject;
29 import org.openhab.binding.heos.internal.json.dto.HeosResponseObject;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
33 import com.google.gson.FieldNamingPolicy;
34 import com.google.gson.Gson;
35 import com.google.gson.GsonBuilder;
38 * Parser used for parsing the responses of JSON cli
40 * @author Martin van Wingerden - Initial contribution
43 public class HeosJsonParser {
44 private final Logger logger = LoggerFactory.getLogger(HeosJsonParser.class);
46 private final Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
49 public HeosEventObject parseEvent(String jsonBody) {
50 HeosJsonWrapper wrapper = gson.fromJson(jsonBody, HeosJsonWrapper.class);
52 return postProcess(wrapper.heos);
55 private HeosEventObject postProcess(HeosJsonObject heos) {
56 return new HeosEventObject(HeosEvent.valueOfString(heos.command), heos.command, splitQuery(heos.message));
59 public <T> HeosResponseObject<T> parseResponse(String jsonBody, Class<T> clazz) {
60 HeosJsonWrapper wrapper = gson.fromJson(jsonBody, HeosJsonWrapper.class);
61 return postProcess(Objects.requireNonNull(wrapper), clazz);
64 private <T> HeosResponseObject<T> postProcess(HeosJsonWrapper wrapper, Class<T> clazz) {
65 T payload = gson.fromJson(wrapper.payload, clazz);
67 return new HeosResponseObject<>(HeosCommandTuple.valueOf(wrapper.heos.command), wrapper.heos.command,
68 wrapper.heos.result, splitQuery(wrapper.heos.message), payload, wrapper.options);
71 private Map<String, String> splitQuery(@Nullable String url) {
73 return Collections.emptyMap();
76 return Arrays.stream(url.split("&")).map(p -> p.split("=", 2))
77 .collect(Collectors.toMap(v -> decode(v[0]), v -> v.length == 1 ? "" : decode(v[1]), this::merge));
81 * for duplicates we ignore the first one
83 * @param v1 first occurrence
84 * @param v2 second occurrence
85 * @return second occurrence
87 private String merge(String v1, String v2) {
88 logger.debug("Ignoring first occurrence '{}' in favor of '{}'", v1, v2);
92 private static String decode(String encoded) {
94 return URLDecoder.decode(encoded, StandardCharsets.UTF_8.name());
95 } catch (UnsupportedEncodingException e) {
96 throw new IllegalStateException("Impossible: UTF-8 is a required encoding", e);