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 GsonColonDateTypeAdapter} class is a custom TypeAdapter factory to ensure deserialization always returns a
34 * list even if the Json document contains only a single curfew object and not an array.
36 * See https://stackoverflow.com/questions/43412261/make-gson-accept-single-objects-where-it-expects-arrays
38 * @author Rene Scherer - Initial contribution
41 public final class SurePetcareDeviceCurfewListTypeAdapterFactory<E> implements TypeAdapterFactory {
43 // Gson can instantiate it itself
44 private SurePetcareDeviceCurfewListTypeAdapterFactory() {
48 public @Nullable <T> TypeAdapter<T> create(final @Nullable Gson gson, final @Nullable TypeToken<T> typeToken) {
49 if (gson == null || typeToken == null) {
52 final TypeAdapter<SurePetcareDeviceCurfew> elementTypeAdapter = gson
53 .getAdapter(TypeToken.get(SurePetcareDeviceCurfew.class));
54 @SuppressWarnings("unchecked")
55 final TypeAdapter<T> alwaysListTypeAdapter = (TypeAdapter<T>) new SurePetcareDeviceCurfewListTypeAdapter(
56 elementTypeAdapter).nullSafe();
57 return alwaysListTypeAdapter;
60 private static final class SurePetcareDeviceCurfewListTypeAdapter
61 extends TypeAdapter<List<SurePetcareDeviceCurfew>> {
63 private final TypeAdapter<SurePetcareDeviceCurfew> elementTypeAdapter;
65 private SurePetcareDeviceCurfewListTypeAdapter(final TypeAdapter<SurePetcareDeviceCurfew> elementTypeAdapter) {
66 this.elementTypeAdapter = elementTypeAdapter;
70 public void write(final @Nullable JsonWriter out, @Nullable final List<SurePetcareDeviceCurfew> list)
72 if (out != null && list != null) {
74 for (SurePetcareDeviceCurfew curfew : list) {
75 elementTypeAdapter.write(out, curfew);
82 public @Nullable List<SurePetcareDeviceCurfew> read(final @Nullable JsonReader in) throws IOException {
83 // This is where we detect the list "type"
84 final SurePetcareDeviceCurfewList list = new SurePetcareDeviceCurfewList();
86 final JsonToken token = in.peek();
89 // If it's a regular list, just consume [, <all elements>, and ]
91 while (in.hasNext()) {
92 SurePetcareDeviceCurfew cf = elementTypeAdapter.read(in);
100 SurePetcareDeviceCurfew cf = elementTypeAdapter.read(in);
106 throw new AssertionError(
107 "Must never happen: check if the type adapter configured with .nullSafe()");
115 throw new MalformedJsonException("Unexpected token: " + token);
117 throw new AssertionError("Must never happen: " + token);