001/*
002 * VM-Operator
003 * Copyright (C) 2024 Michael N. Lipp
004 * 
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Affero General Public License as
007 * published by the Free Software Foundation, either version 3 of the
008 * License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013 * GNU Affero General Public License for more details.
014 *
015 * You should have received a copy of the GNU Affero General Public License
016 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
017 */
018
019package org.jdrupes.vmoperator.common;
020
021import com.google.gson.Gson;
022import com.google.gson.InstanceCreator;
023import com.google.gson.JsonObject;
024import com.google.gson.TypeAdapter;
025import com.google.gson.TypeAdapterFactory;
026import com.google.gson.reflect.TypeToken;
027import com.google.gson.stream.JsonReader;
028import com.google.gson.stream.JsonWriter;
029import io.kubernetes.client.openapi.ApiClient;
030import java.io.IOException;
031import java.lang.reflect.InvocationTargetException;
032import java.lang.reflect.Type;
033
034/**
035 * A factory for creating objects.
036 *
037 * @param <O> the generic type
038 * @param <L> the generic type
039 */
040public class DynamicTypeAdapterFactory<O extends K8sDynamicModel,
041        L extends K8sDynamicModelsBase<O>> implements TypeAdapterFactory {
042
043    private final Class<O> objectClass;
044    private final Class<L> objectListClass;
045
046    /**
047     * Make sure that this adapter is registered.
048     *
049     * @param client the client
050     */
051    public void register(ApiClient client) {
052        if (!ModelCreator.class
053            .equals(client.getJSON().getGson().getAdapter(objectClass)
054                .getClass())
055            || !ModelsCreator.class.equals(client.getJSON().getGson()
056                .getAdapter(objectListClass).getClass())) {
057            Gson gson = client.getJSON().getGson();
058            client.getJSON().setGson(gson.newBuilder()
059                .registerTypeAdapterFactory(this).create());
060        }
061    }
062
063    /**
064     * Instantiates a new generic type adapter factory.
065     *
066     * @param objectClass the object class
067     * @param objectListClass the object list class
068     */
069    public DynamicTypeAdapterFactory(Class<O> objectClass,
070            Class<L> objectListClass) {
071        this.objectClass = objectClass;
072        this.objectListClass = objectListClass;
073    }
074
075    /**
076     * Creates a type adapter for the given type.
077     *
078     * @param <T> the generic type
079     * @param gson the gson
080     * @param typeToken the type token
081     * @return the type adapter or null if the type is not handles by
082     * this factory
083     */
084    @SuppressWarnings("unchecked")
085    @Override
086    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
087        if (TypeToken.get(objectClass).equals(typeToken)) {
088            return (TypeAdapter<T>) new ModelCreator(gson);
089        }
090        if (TypeToken.get(objectListClass).equals(typeToken)) {
091            return (TypeAdapter<T>) new ModelsCreator(gson);
092        }
093        return null;
094    }
095
096    /**
097     * The Class ModelCreator.
098     */
099    private class ModelCreator extends TypeAdapter<O>
100            implements InstanceCreator<O> {
101        private final Gson delegate;
102
103        /**
104         * Instantiates a new object state creator.
105         *
106         * @param delegate the delegate
107         */
108        public ModelCreator(Gson delegate) {
109            this.delegate = delegate;
110        }
111
112        @Override
113        public O createInstance(Type type) {
114            try {
115                return objectClass.getConstructor(Gson.class, JsonObject.class)
116                    .newInstance(delegate, null);
117            } catch (InstantiationException | IllegalAccessException
118                    | IllegalArgumentException | InvocationTargetException
119                    | NoSuchMethodException | SecurityException e) {
120                return null;
121            }
122        }
123
124        @Override
125        public void write(JsonWriter jsonWriter, O state)
126                throws IOException {
127            jsonWriter.jsonValue(delegate.toJson(state.data()));
128        }
129
130        @Override
131        public O read(JsonReader jsonReader)
132                throws IOException {
133            try {
134                return objectClass.getConstructor(Gson.class, JsonObject.class)
135                    .newInstance(delegate,
136                        delegate.fromJson(jsonReader, JsonObject.class));
137            } catch (InstantiationException | IllegalAccessException
138                    | IllegalArgumentException | InvocationTargetException
139                    | NoSuchMethodException | SecurityException e) {
140                return null;
141            }
142        }
143    }
144
145    /**
146     * The Class ModelsCreator.
147     */
148    private class ModelsCreator extends TypeAdapter<L>
149            implements InstanceCreator<L> {
150
151        private final Gson delegate;
152
153        /**
154         * Instantiates a new object states creator.
155         *
156         * @param delegate the delegate
157         */
158        public ModelsCreator(Gson delegate) {
159            this.delegate = delegate;
160        }
161
162        @Override
163        public L createInstance(Type type) {
164            try {
165                return objectListClass
166                    .getConstructor(Gson.class, JsonObject.class)
167                    .newInstance(delegate, null);
168            } catch (InstantiationException | IllegalAccessException
169                    | IllegalArgumentException | InvocationTargetException
170                    | NoSuchMethodException | SecurityException e) {
171                return null;
172            }
173        }
174
175        @Override
176        public void write(JsonWriter jsonWriter, L states)
177                throws IOException {
178            jsonWriter.jsonValue(delegate.toJson(states.data()));
179        }
180
181        @Override
182        public L read(JsonReader jsonReader)
183                throws IOException {
184            try {
185                return objectListClass
186                    .getConstructor(Gson.class, JsonObject.class)
187                    .newInstance(delegate,
188                        delegate.fromJson(jsonReader, JsonObject.class));
189            } catch (InstantiationException | IllegalAccessException
190                    | IllegalArgumentException | InvocationTargetException
191                    | NoSuchMethodException | SecurityException e) {
192                return null;
193            }
194        }
195    }
196
197}