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.surepetcare.internal.utils;
15 import java.io.IOException;
16 import java.util.List;
18 import org.eclipse.jdt.annotation.NonNullByDefault;
19 import org.eclipse.jdt.annotation.Nullable;
20 import org.openhab.binding.surepetcare.internal.dto.SurePetcareDeviceCurfew;
21 import org.openhab.binding.surepetcare.internal.dto.SurePetcareDeviceCurfewList;
23 import com.google.gson.Gson;
24 import com.google.gson.TypeAdapter;
25 import com.google.gson.TypeAdapterFactory;
26 import com.google.gson.reflect.TypeToken;
27 import com.google.gson.stream.JsonReader;
28 import com.google.gson.stream.JsonToken;
29 import com.google.gson.stream.JsonWriter;
30 import com.google.gson.stream.MalformedJsonException;
33 * The {@link SurePetcareDeviceCurfewListTypeAdapterFactory} class is a custom TypeAdapter factory to ensure
34 * deserialization always returns a list even if the Json document contains only a single curfew object and
37 * See https://stackoverflow.com/questions/43412261/make-gson-accept-single-objects-where-it-expects-arrays
39 * @author Rene Scherer - Initial contribution
42 public final class SurePetcareDeviceCurfewListTypeAdapterFactory<E> implements TypeAdapterFactory {
44 // Gson can instantiate it itself
45 private SurePetcareDeviceCurfewListTypeAdapterFactory() {
49 public @Nullable <T> TypeAdapter<T> create(final @Nullable Gson gson, final @Nullable TypeToken<T> typeToken) {
50 if (gson == null || typeToken == null) {
53 final TypeAdapter<SurePetcareDeviceCurfew> elementTypeAdapter = gson
54 .getAdapter(TypeToken.get(SurePetcareDeviceCurfew.class));
55 @SuppressWarnings("unchecked")
56 final TypeAdapter<T> alwaysListTypeAdapter = (TypeAdapter<T>) new SurePetcareDeviceCurfewListTypeAdapter(
57 elementTypeAdapter).nullSafe();
58 return alwaysListTypeAdapter;
61 private static final class SurePetcareDeviceCurfewListTypeAdapter
62 extends TypeAdapter<List<SurePetcareDeviceCurfew>> {
64 private final TypeAdapter<SurePetcareDeviceCurfew> elementTypeAdapter;
66 private SurePetcareDeviceCurfewListTypeAdapter(final TypeAdapter<SurePetcareDeviceCurfew> elementTypeAdapter) {
67 this.elementTypeAdapter = elementTypeAdapter;
71 public void write(final @Nullable JsonWriter out, @Nullable final List<SurePetcareDeviceCurfew> list)
73 if (out != null && list != null) {
75 for (SurePetcareDeviceCurfew curfew : list) {
76 elementTypeAdapter.write(out, curfew);
83 public @Nullable List<SurePetcareDeviceCurfew> read(final @Nullable JsonReader in) throws IOException {
84 // This is where we detect the list "type"
85 final SurePetcareDeviceCurfewList list = new SurePetcareDeviceCurfewList();
87 final JsonToken token = in.peek();
90 // If it's a regular list, just consume [, <all elements>, and ]
92 while (in.hasNext()) {
93 SurePetcareDeviceCurfew cf = elementTypeAdapter.read(in);
101 SurePetcareDeviceCurfew cf = elementTypeAdapter.read(in);
107 throw new AssertionError(
108 "Must never happen: check if the type adapter configured with .nullSafe()");
116 throw new MalformedJsonException("Unexpected token: " + token);
118 throw new AssertionError("Must never happen: " + token);