summaryrefslogtreecommitdiffstats
path: root/gson/src/test/java
diff options
context:
space:
mode:
Diffstat (limited to 'gson/src/test/java')
-rw-r--r--gson/src/test/java/org/onap/policy/common/gson/GsonMessageBodyHandlerTest.java156
-rw-r--r--gson/src/test/java/org/onap/policy/common/gson/JacksonExclusionStrategyTest.java203
-rw-r--r--gson/src/test/java/org/onap/policy/common/gson/internal/AdapterTest.java387
-rw-r--r--gson/src/test/java/org/onap/policy/common/gson/internal/ClassWalkerTest.java507
-rw-r--r--gson/src/test/java/org/onap/policy/common/gson/internal/DataAdapterFactory.java310
5 files changed, 1563 insertions, 0 deletions
diff --git a/gson/src/test/java/org/onap/policy/common/gson/GsonMessageBodyHandlerTest.java b/gson/src/test/java/org/onap/policy/common/gson/GsonMessageBodyHandlerTest.java
new file mode 100644
index 00000000..85ecfea4
--- /dev/null
+++ b/gson/src/test/java/org/onap/policy/common/gson/GsonMessageBodyHandlerTest.java
@@ -0,0 +1,156 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.common.gson;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import javax.ws.rs.core.MediaType;
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.policy.common.gson.GsonMessageBodyHandler;
+
+public class GsonMessageBodyHandlerTest {
+ private static final String GEN_TYPE = "some-type";
+ private static final String[] subtypes = {"json", "jSoN", "hello+json", "javascript", "x-javascript", "x-json"};
+
+ @SuppressWarnings("rawtypes")
+ private static final Class GEN_CLASS = MyObject.class;
+
+ @SuppressWarnings("unchecked")
+ private static final Class<Object> CLASS_OBJ = GEN_CLASS;
+
+ private GsonMessageBodyHandler hdlr;
+
+ @Before
+ public void setUp() {
+ hdlr = new GsonMessageBodyHandler();
+ }
+
+ @Test
+ public void testIsWriteable() {
+ // null media type
+ assertTrue(hdlr.isWriteable(null, null, null, null));
+
+ for (String subtype : subtypes) {
+ assertTrue("writeable " + subtype, hdlr.isWriteable(null, null, null, new MediaType(GEN_TYPE, subtype)));
+
+ }
+
+ // the remaining should be FALSE
+
+ // null subtype
+ assertFalse(hdlr.isWriteable(null, null, null, new MediaType(GEN_TYPE, null)));
+
+ // text subtype
+ assertFalse(hdlr.isWriteable(null, null, null, MediaType.TEXT_HTML_TYPE));
+ }
+
+ @Test
+ public void testGetSize() {
+ assertEquals(-1, hdlr.getSize(null, null, null, null, null));
+ }
+
+ @Test
+ public void testWriteTo_testReadFrom() throws Exception {
+ ByteArrayOutputStream outstr = new ByteArrayOutputStream();
+ MyObject obj1 = new MyObject(10);
+ hdlr.writeTo(obj1, obj1.getClass(), CLASS_OBJ, null, null, null, outstr);
+
+ Object obj2 = hdlr.readFrom(CLASS_OBJ, CLASS_OBJ, null, null, null,
+ new ByteArrayInputStream(outstr.toByteArray()));
+ assertEquals(obj1.toString(), obj2.toString());
+ }
+
+ @Test
+ public void testWriteTo_DifferentTypes() throws Exception {
+ ByteArrayOutputStream outstr = new ByteArrayOutputStream();
+
+ // use a derived type, but specify the base type when writing
+ MyObject obj1 = new MyObject(10) {};
+ hdlr.writeTo(obj1, obj1.getClass(), CLASS_OBJ, null, null, null, outstr);
+
+ Object obj2 = hdlr.readFrom(CLASS_OBJ, CLASS_OBJ, null, null, null,
+ new ByteArrayInputStream(outstr.toByteArray()));
+ assertEquals(obj1.toString(), obj2.toString());
+ }
+
+ @Test
+ public void testIsReadable() {
+ // null media type
+ assertTrue(hdlr.isReadable(null, null, null, null));
+
+ // null subtype
+ assertFalse(hdlr.isReadable(null, null, null, new MediaType(GEN_TYPE, null)));
+
+ for (String subtype : subtypes) {
+ assertTrue("readable " + subtype, hdlr.isReadable(null, null, null, new MediaType(GEN_TYPE, subtype)));
+
+ }
+
+ // the remaining should be FALSE
+
+ // null subtype
+ assertFalse(hdlr.isReadable(null, null, null, new MediaType(GEN_TYPE, null)));
+
+ // text subtype
+ assertFalse(hdlr.isReadable(null, null, null, MediaType.TEXT_HTML_TYPE));
+ }
+
+ @Test
+ public void testReadFrom_DifferentTypes() throws Exception {
+ ByteArrayOutputStream outstr = new ByteArrayOutputStream();
+ MyObject obj1 = new MyObject(10);
+ hdlr.writeTo(obj1, obj1.getClass(), CLASS_OBJ, null, null, null, outstr);
+
+ // use a derived type, but specify the base type when reading
+ @SuppressWarnings("rawtypes")
+ Class clazz = new MyObject() {}.getClass();
+
+ @SuppressWarnings("unchecked")
+ Class<Object> objclazz = clazz;
+
+ Object obj2 = hdlr.readFrom(objclazz, CLASS_OBJ, null, null, null,
+ new ByteArrayInputStream(outstr.toByteArray()));
+ assertEquals(obj1.toString(), obj2.toString());
+ }
+
+ public static class MyObject {
+ private int id;
+
+ public MyObject() {
+ super();
+ }
+
+ public MyObject(int id) {
+ this.id = id;
+ }
+
+ @Override
+ public String toString() {
+ return "MyObject [id=" + id + "]";
+ }
+ }
+
+}
diff --git a/gson/src/test/java/org/onap/policy/common/gson/JacksonExclusionStrategyTest.java b/gson/src/test/java/org/onap/policy/common/gson/JacksonExclusionStrategyTest.java
new file mode 100644
index 00000000..4b5473c5
--- /dev/null
+++ b/gson/src/test/java/org/onap/policy/common/gson/JacksonExclusionStrategyTest.java
@@ -0,0 +1,203 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.common.gson;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.google.gson.FieldAttributes;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonElement;
+import java.lang.reflect.GenericArrayType;
+import java.util.LinkedList;
+import java.util.TreeMap;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.onap.policy.common.gson.JacksonExclusionStrategy;
+
+public class JacksonExclusionStrategyTest {
+
+ private static JacksonExclusionStrategy strategy;
+ private static Gson gson;
+
+ @BeforeClass
+ public static void setUpBeforeClass() {
+ strategy = new JacksonExclusionStrategy();
+ gson = new GsonBuilder().setExclusionStrategies(strategy).create();
+ }
+
+ @Test
+ public void testWithGson() {
+ Derived data = new Derived();
+ data.setId(10);
+ data.setText("some text");
+ data.setValue("some value");
+
+ // no fields should be serialized
+ String result = gson.toJson(data);
+ assertEquals("{}", result);
+
+ // no fields should be deserialized
+ result = "{'id':20, 'text':'my text', 'value':'my value'}".replace('\'', '"');
+ Derived data2 = gson.fromJson(result, Derived.class);
+ assertEquals(new Derived().toString(), data2.toString());
+ }
+
+ @Test
+ public void testShouldSkipField() throws Exception {
+ // should skip every field of Data
+ assertTrue(strategy.shouldSkipField(new FieldAttributes(Data.class.getDeclaredField("id"))));
+ assertTrue(strategy.shouldSkipField(new FieldAttributes(Data.class.getDeclaredField("text"))));
+
+ // should not skip fields in Map
+ assertFalse(strategy.shouldSkipField(new FieldAttributes(MyMap.class.getDeclaredField("mapId"))));
+ }
+
+ @Test
+ public void testShouldSkipClass() {
+ assertFalse(strategy.shouldSkipClass(null));
+ assertFalse(strategy.shouldSkipClass(Object.class));
+ }
+
+ @Test
+ public void testIsManaged() {
+ assertTrue(JacksonExclusionStrategy.isManaged(Data.class));
+ assertTrue(JacksonExclusionStrategy.isManaged(Intfc.class));
+ assertTrue(JacksonExclusionStrategy.isManaged(com.google.gson.TypeAdapter.class));
+
+ // generic classes
+ assertFalse(JacksonExclusionStrategy.isManaged(new Data[0].getClass()));
+ assertFalse(JacksonExclusionStrategy.isManaged(Enum.class));
+ assertFalse(JacksonExclusionStrategy.isManaged(boolean.class));
+ assertFalse(JacksonExclusionStrategy.isManaged(byte.class));
+ assertFalse(JacksonExclusionStrategy.isManaged(short.class));
+ assertFalse(JacksonExclusionStrategy.isManaged(int.class));
+ assertFalse(JacksonExclusionStrategy.isManaged(long.class));
+ assertFalse(JacksonExclusionStrategy.isManaged(float.class));
+ assertFalse(JacksonExclusionStrategy.isManaged(double.class));
+ assertFalse(JacksonExclusionStrategy.isManaged(char.class));
+ assertFalse(JacksonExclusionStrategy.isManaged(Boolean.class));
+ assertFalse(JacksonExclusionStrategy.isManaged(Byte.class));
+ assertFalse(JacksonExclusionStrategy.isManaged(Short.class));
+ assertFalse(JacksonExclusionStrategy.isManaged(Integer.class));
+ assertFalse(JacksonExclusionStrategy.isManaged(Long.class));
+ assertFalse(JacksonExclusionStrategy.isManaged(Float.class));
+ assertFalse(JacksonExclusionStrategy.isManaged(Double.class));
+ assertFalse(JacksonExclusionStrategy.isManaged(Character.class));
+ assertFalse(JacksonExclusionStrategy.isManaged(String.class));
+ assertFalse(JacksonExclusionStrategy.isManaged(MyMap.class));
+ assertFalse(JacksonExclusionStrategy.isManaged(MyList.class));
+ assertFalse(JacksonExclusionStrategy.isManaged(MyJson.class));
+ assertFalse(JacksonExclusionStrategy.isManaged(GenericArrayType.class));
+ }
+
+ /**
+ * Used to verify that no fields are exposed.
+ */
+ public static class Data {
+ private int id;
+ public String text;
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ @Override
+ public String toString() {
+ return "Data [id=" + id + ", text=" + text + "]";
+ }
+ }
+
+ public static class Derived extends Data {
+ protected String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ return "Derived [value=" + value + ", " + super.toString() + "]";
+ }
+ }
+
+ /**
+ * Used to verify that enums are not managed.
+ */
+ public static enum Enum {
+ UP, DOWN,
+ }
+
+ /**
+ * Used to verify that interfaces <i>are</i> managed.
+ */
+ public static interface Intfc {
+ int getId();
+ }
+
+ /**
+ * Used to verify that Maps are not managed.
+ */
+ public static class MyMap extends TreeMap<String, Data> {
+ private static final long serialVersionUID = 1L;
+
+ private int mapId;
+
+ public int getMapId() {
+ return mapId;
+ }
+ }
+
+ /**
+ * Used to verify that Collections are not managed.
+ */
+ public static class MyList extends LinkedList<Data> {
+ private static final long serialVersionUID = 1L;
+ }
+
+ /**
+ * Used to verify that JsonElements are not managed.
+ */
+ public static class MyJson extends JsonElement {
+ @Override
+ public JsonElement deepCopy() {
+ return null;
+ }
+ }
+}
diff --git a/gson/src/test/java/org/onap/policy/common/gson/internal/AdapterTest.java b/gson/src/test/java/org/onap/policy/common/gson/internal/AdapterTest.java
new file mode 100644
index 00000000..fcb0d9ad
--- /dev/null
+++ b/gson/src/test/java/org/onap/policy/common/gson/internal/AdapterTest.java
@@ -0,0 +1,387 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.common.gson.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonPrimitive;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.List;
+import org.junit.Test;
+import org.onap.policy.common.gson.JacksonExclusionStrategy;
+import org.onap.policy.common.gson.annotation.GsonJsonProperty;
+import org.onap.policy.common.gson.internal.Adapter;
+import org.onap.policy.common.gson.internal.DataAdapterFactory.Data;
+import org.onap.policy.common.gson.internal.DataAdapterFactory.DerivedData;
+
+public class AdapterTest {
+ private static final String GET_VALUE_NAME = "getValue";
+ private static final String VALUE_NAME = "value";
+ private static final String MY_NAME = AdapterTest.class.getName();
+
+ private static DataAdapterFactory dataAdapter = new DataAdapterFactory();
+
+ private static Gson gson = new GsonBuilder().registerTypeAdapterFactory(dataAdapter)
+ .setExclusionStrategies(new JacksonExclusionStrategy()).create();
+
+ /*
+ * The remaining fields are just used within the tests.
+ */
+
+ private String value;
+
+ // empty alias - should use field name
+ @GsonJsonProperty("")
+ protected String emptyAlias;
+
+ @GsonJsonProperty("name-with-alias")
+ protected String nameWithAlias;
+
+ protected String unaliased;
+
+ protected String $invalidFieldName;
+
+ private List<Data> listField;
+
+ private Data dataField;
+
+
+ @Test
+ public void testIsManagedField() {
+ assertTrue(Adapter.isManaged(field(VALUE_NAME)));
+
+ assertFalse(Adapter.isManaged(field("$invalidFieldName")));
+ }
+
+ @Test
+ public void testIsManagedMethod() {
+ assertTrue(Adapter.isManaged(mget(GET_VALUE_NAME)));
+
+ assertFalse(Adapter.isManaged(mget("get$InvalidName")));
+ assertFalse(Adapter.isManaged(mset("set$InvalidName")));
+ }
+
+ @Test
+ public void testAdapterField_Converter() {
+ Adapter adapter = new Adapter(gson, field("dataField"));
+
+ // first, write something of type Data
+ dataAdapter.reset();
+ dataField = new Data(300);
+ JsonElement tree = adapter.toJsonTree(dataField);
+ assertEquals("{'id':300}".replace('\'', '"'), tree.toString());
+
+ // now try a subclass
+ dataAdapter.reset();
+ dataField = new DerivedData(300, "three");
+ tree = adapter.toJsonTree(dataField);
+ assertEquals("{'id':300,'text':'three'}".replace('\'', '"'), tree.toString());
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testAdapterField_Converter_List() {
+ listField = DataAdapterFactory.makeList();
+
+ Adapter adapter = new Adapter(gson, field("listField"));
+
+ dataAdapter.reset();
+ JsonElement tree = adapter.toJsonTree(listField);
+ assertTrue(dataAdapter.isDataWritten());
+ assertEquals(DataAdapterFactory.ENCODED_LIST, tree.toString());
+
+ // encode it twice so it uses the cached converter
+ dataAdapter.reset();
+ tree = adapter.toJsonTree(listField);
+ assertTrue(dataAdapter.isDataWritten());
+ assertEquals(DataAdapterFactory.ENCODED_LIST, tree.toString());
+
+ dataAdapter.reset();
+ List<Data> lst2 = (List<Data>) adapter.fromJsonTree(tree);
+ assertTrue(dataAdapter.isDataRead());
+
+ assertEquals(listField.toString(), lst2.toString());
+
+ // decode it twice so it uses the cached converter
+ dataAdapter.reset();
+ lst2 = (List<Data>) adapter.fromJsonTree(tree);
+ assertTrue(dataAdapter.isDataRead());
+
+ assertEquals(listField.toString(), lst2.toString());
+ }
+
+ @Test
+ public void testAdapterMethod_Converter() throws Exception {
+ listField = DataAdapterFactory.makeList();
+
+ Method getter = mget("getMyList");
+
+ Adapter aget = new Adapter(gson, getter, true, getter.getReturnType());
+
+ dataAdapter.reset();
+ JsonElement tree = aget.toJsonTree(listField);
+ assertTrue(dataAdapter.isDataWritten());
+ assertEquals(DataAdapterFactory.ENCODED_LIST, tree.toString());
+
+ Method setter = AdapterTest.class.getDeclaredMethod("setMyList", List.class);
+ Adapter aset = new Adapter(gson, setter, true, setter.getGenericParameterTypes()[0]);
+
+ dataAdapter.reset();
+ @SuppressWarnings("unchecked")
+ List<Data> lst2 = (List<Data>) aset.fromJsonTree(tree);
+ assertTrue(dataAdapter.isDataRead());
+
+ assertEquals(listField.toString(), lst2.toString());
+ }
+
+ @Test
+ public void testGetPropName_testGetFullName_testMakeError() {
+ // test field
+ Adapter adapter = new Adapter(gson, field(VALUE_NAME));
+
+ assertEquals(VALUE_NAME, adapter.getPropName());
+ assertEquals(MY_NAME + ".value", adapter.getFullName());
+
+
+ // test getter
+ adapter = new Adapter(gson, mget(GET_VALUE_NAME), true, String.class);
+
+ assertEquals(VALUE_NAME, adapter.getPropName());
+ assertEquals(MY_NAME + ".getValue", adapter.getFullName());
+
+ assertEquals("hello: " + MY_NAME + ".getValue", adapter.makeError("hello: "));
+
+
+ // test setter
+ adapter = new Adapter(gson, mset("setValue"), false, String.class);
+
+ assertEquals(VALUE_NAME, adapter.getPropName());
+ assertEquals(MY_NAME + ".setValue", adapter.getFullName());
+ }
+
+ @Test
+ public void testToJsonTree() {
+ Adapter adapter = new Adapter(gson, field(VALUE_NAME));
+
+ JsonElement tree = adapter.toJsonTree("hello");
+ assertTrue(tree.isJsonPrimitive());
+ assertEquals("hello", tree.getAsString());
+ }
+
+ @Test
+ public void testFromJsonTree() {
+ Adapter adapter = new Adapter(gson, field(VALUE_NAME));
+
+ assertEquals("world", adapter.fromJsonTree(new JsonPrimitive("world")));
+ }
+
+ @Test
+ public void testDetmPropName() {
+ assertEquals("emptyAlias", Adapter.detmPropName(field("emptyAlias")));
+ assertEquals("name-with-alias", Adapter.detmPropName(field("nameWithAlias")));
+ assertEquals("unaliased", Adapter.detmPropName(field("unaliased")));
+ assertEquals(null, Adapter.detmPropName(field("$invalidFieldName")));
+ }
+
+ @Test
+ public void testDetmGetterPropName() {
+ assertEquals("emptyAlias", Adapter.detmGetterPropName(mget("getEmptyAlias")));
+ assertEquals("get-with-alias", Adapter.detmGetterPropName(mget("getWithAlias")));
+ assertEquals("plain", Adapter.detmGetterPropName(mget("getPlain")));
+ assertEquals("primBool", Adapter.detmGetterPropName(mget("isPrimBool")));
+ assertEquals("boxedBool", Adapter.detmGetterPropName(mget("isBoxedBool")));
+ assertEquals(null, Adapter.detmGetterPropName(mget("isString")));
+ assertEquals(null, Adapter.detmGetterPropName(mget("noGet")));
+ assertEquals(null, Adapter.detmGetterPropName(mget("get")));
+ assertEquals(null, Adapter.detmGetterPropName(mget("get$InvalidName")));
+ }
+
+ @Test
+ public void testDetmSetterPropName() {
+ assertEquals("emptyAlias", Adapter.detmSetterPropName(mset("setEmptyAlias")));
+ assertEquals("set-with-alias", Adapter.detmSetterPropName(mset("setWithAlias")));
+ assertEquals("plain", Adapter.detmSetterPropName(mset("setPlain")));
+ assertEquals(null, Adapter.detmSetterPropName(mset("noSet")));
+ assertEquals(null, Adapter.detmSetterPropName(mset("set")));
+ assertEquals(null, Adapter.detmSetterPropName(mset("set$InvalidName")));
+ }
+
+ @Test
+ public void testGetQualifiedNameField() throws Exception {
+ assertEquals(MY_NAME + ".value", Adapter.getQualifiedName(AdapterTest.class.getDeclaredField(VALUE_NAME)));
+ }
+
+ @Test
+ public void testGetQualifiedNameMethod() throws Exception {
+ assertEquals(MY_NAME + ".getValue", Adapter.getQualifiedName(mget(GET_VALUE_NAME)));
+ }
+
+ /**
+ * Gets a field from this class, by name.
+ *
+ * @param name name of the field to get
+ * @return the field
+ */
+ private Field field(String name) {
+ try {
+ return AdapterTest.class.getDeclaredField(name);
+
+ } catch (SecurityException | NoSuchFieldException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Gets a "getter" method from this class, by name.
+ *
+ * @param name name of the method to get
+ * @return the method
+ */
+ private Method mget(String name) {
+ try {
+ return AdapterTest.class.getDeclaredMethod(name);
+
+ } catch (NoSuchMethodException | SecurityException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Gets a "setter" method from this class, by name.
+ *
+ * @param name name of the method to get
+ * @return the method
+ */
+ private Method mset(String name) {
+ try {
+ return AdapterTest.class.getDeclaredMethod(name, String.class);
+
+ } catch (NoSuchMethodException | SecurityException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /*
+ * The remaining methods are just used within the tests.
+ */
+
+ protected String getValue() {
+ return value;
+ }
+
+ // empty alias - should use method name
+ @GsonJsonProperty("")
+ protected String getEmptyAlias() {
+ return "";
+ }
+
+ @GsonJsonProperty("get-with-alias")
+ protected String getWithAlias() {
+ return "";
+ }
+
+ // no alias, begins with "get"
+ protected String getPlain() {
+ return "";
+ }
+
+ // begins with "is", returns primitive boolean
+ protected boolean isPrimBool() {
+ return true;
+ }
+
+ // begins with "is", returns boxed Boolean
+ protected Boolean isBoxedBool() {
+ return true;
+ }
+
+ // begins with "is", but doesn't return a boolean
+ protected String isString() {
+ return "";
+ }
+
+ // doesn't begin with "get"
+ protected String noGet() {
+ return "";
+ }
+
+ // nothing after "get"
+ protected String get() {
+ return "";
+ }
+
+ // name has a bogus character
+ protected String get$InvalidName() {
+ return "";
+ }
+
+
+ protected void setValue(String text) {
+ // do nothing
+ }
+
+ // empty alias - should use method name
+ @GsonJsonProperty("")
+ protected void setEmptyAlias(String text) {
+ // do nothing
+ }
+
+ @GsonJsonProperty("set-with-alias")
+ protected void setWithAlias(String text) {
+ // do nothing
+ }
+
+ // no alias, begins with "set"
+ protected void setPlain(String text) {
+ // do nothing
+ }
+
+ // doesn't begin with "set"
+ protected void noSet(String text) {
+ // do nothing
+ }
+
+ // nothing after "get"
+ protected void set(String text) {
+ // do nothing
+ }
+
+ // name has a bogus character
+ protected void set$InvalidName(String text) {
+ // do nothing
+ }
+
+ // returns a list
+ protected List<Data> getMyList() {
+ return listField;
+ }
+
+ // accepts a list
+ protected void setMyList(List<Data> newList) {
+ listField = newList;
+ }
+}
diff --git a/gson/src/test/java/org/onap/policy/common/gson/internal/ClassWalkerTest.java b/gson/src/test/java/org/onap/policy/common/gson/internal/ClassWalkerTest.java
new file mode 100644
index 00000000..1a15be09
--- /dev/null
+++ b/gson/src/test/java/org/onap/policy/common/gson/internal/ClassWalkerTest.java
@@ -0,0 +1,507 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.common.gson.internal;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import com.google.gson.JsonParseException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.stream.Collectors;
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.policy.common.gson.annotation.GsonJsonAnyGetter;
+import org.onap.policy.common.gson.annotation.GsonJsonAnySetter;
+import org.onap.policy.common.gson.annotation.GsonJsonIgnore;
+import org.onap.policy.common.gson.annotation.GsonJsonProperty;
+import org.onap.policy.common.gson.internal.Adapter;
+import org.onap.policy.common.gson.internal.ClassWalker;
+
+public class ClassWalkerTest {
+
+ private MyWalker walker;
+
+ /**
+ * Set up.
+ */
+ @Before
+ public void setUp() {
+ walker = new MyWalker();
+ }
+
+ @Test
+ public void testExamineClassOfQ_testExamineField_testExamineInField_testExamineOutField() {
+ walker.walkClassHierarchy(DerivedFromBottom.class);
+
+ assertEquals("[Intfc1, Intfc2, Intfc1, Intfc3, Bottom, DerivedFromBottom]", walker.classes.toString());
+
+ List<String> inFields = walker.getInProps(Field.class).stream().map(field -> field.getName())
+ .collect(Collectors.toList());
+ Collections.sort(inFields);
+ assertEquals("[exposedField, overriddenValue, transField]", inFields.toString());
+
+ List<String> outFields = walker.getInProps(Field.class).stream().map(field -> field.getName())
+ .collect(Collectors.toList());
+ Collections.sort(outFields);
+ assertEquals("[exposedField, overriddenValue, transField]", outFields.toString());
+
+ // should work with interfaces without throwing an NPE
+ walker.walkClassHierarchy(Intfc1.class);
+ }
+
+ @Test
+ public void testHasAnyGetter() {
+ walker.walkClassHierarchy(Object.class);
+ assertNull(walker.getAnyGetter());
+ assertNull(walker.getAnySetter());
+
+ walker.walkClassHierarchy(AnyGetterIgnored.class);
+ assertNull(walker.getAnyGetter());
+ assertNull(walker.getAnySetter());
+
+ walker.walkClassHierarchy(AnyGetterOnly.class);
+ assertNotNull(walker.getAnyGetter());
+ assertNull(walker.getAnySetter());
+ }
+
+ @Test
+ public void testHasAnySetter() {
+ walker.walkClassHierarchy(Object.class);
+ assertNull(walker.getAnySetter());
+ assertNull(walker.getAnyGetter());
+
+ walker.walkClassHierarchy(AnySetterIgnored.class);
+ assertNull(walker.getAnySetter());
+ assertNull(walker.getAnyGetter());
+
+ walker.walkClassHierarchy(AnySetterOnly.class);
+ assertNotNull(walker.getAnySetter());
+ assertNull(walker.getAnyGetter());
+ }
+
+ @Test
+ public void testExamineMethod() {
+ walker.walkClassHierarchy(DerivedFromData.class);
+
+ assertEquals("[Data, DerivedFromData]", walker.classes.toString());
+
+ // ensure all methods were examined
+ Collections.sort(walker.methods);
+ List<String> lst = Arrays.asList("getId", "getValue", "getOnlyOut", "getStatic", "getText", "getTheMap",
+ "getUnserialized", "getValue", "getWithParams", "setExtraParams", "setId", "setMap",
+ "setMapValue", "setMissingParams", "setNonPublic", "setOnlyIn", "setText", "setUnserialized",
+ "setValue", "setValue", "wrongGetPrefix", "wrongSetPrefix");
+ Collections.sort(lst);
+ assertEquals(lst.toString(), walker.methods.toString());
+
+ assertNotNull(walker.getAnyGetter());
+ assertEquals("getTheMap", walker.getAnyGetter().getName());
+
+ List<String> getters = walker.getOutProps(Method.class).stream().map(method -> method.getName())
+ .collect(Collectors.toList());
+ Collections.sort(getters);
+ assertEquals("[getId, getOnlyOut, getValue]", getters.toString());
+
+ assertNotNull(walker.getAnySetter());
+ assertEquals("setMapValue", walker.getAnySetter().getName());
+
+ List<String> setters = walker.getInProps(Method.class).stream().map(method -> method.getName())
+ .collect(Collectors.toList());
+ Collections.sort(setters);
+ assertEquals("[setId, setOnlyIn, setValue]", setters.toString());
+
+ // getter with invalid parameter count
+ assertThatThrownBy(() -> walker.walkClassHierarchy(AnyGetterMismatchParams.class))
+ .isInstanceOf(JsonParseException.class).hasMessage(ClassWalker.ANY_GETTER_MISMATCH_ERR
+ + AnyGetterMismatchParams.class.getName() + ".getTheMap");
+
+ // setter with too few parameters
+ assertThatThrownBy(() -> walker.walkClassHierarchy(AnySetterTooFewParams.class))
+ .isInstanceOf(JsonParseException.class).hasMessage(ClassWalker.ANY_SETTER_MISMATCH_ERR
+ + AnySetterTooFewParams.class.getName() + ".setOverride");
+
+ // setter with too many parameters
+ assertThatThrownBy(() -> walker.walkClassHierarchy(AnySetterTooManyParams.class))
+ .isInstanceOf(JsonParseException.class).hasMessage(ClassWalker.ANY_SETTER_MISMATCH_ERR
+ + AnySetterTooManyParams.class.getName() + ".setOverride");
+
+ // setter with invalid parameter type
+ assertThatThrownBy(() -> walker.walkClassHierarchy(AnySetterInvalidParam.class))
+ .isInstanceOf(JsonParseException.class).hasMessage(ClassWalker.ANY_SETTER_TYPE_ERR
+ + AnySetterInvalidParam.class.getName() + ".setOverride");
+ }
+
+ @Test
+ public void testExamineMethod_AnyGetter() {
+ walker.walkClassHierarchy(AnyGetterOverride.class);
+
+ assertNotNull(walker.getAnyGetter());
+ assertEquals("getOverride", walker.getAnyGetter().getName());
+ }
+
+ @Test
+ public void testExamineMethod_AnySetter() {
+ walker.walkClassHierarchy(AnySetterOverride.class);
+
+ assertNotNull(walker.getAnySetter());
+ assertEquals("setOverride", walker.getAnySetter().getName());
+ }
+
+ @Test
+ public void testGetInNotIgnored_testGetOutNotIgnored() {
+ walker.walkClassHierarchy(DerivedFromData.class);
+
+ assertEquals("[id, onlyIn, text, value]", new TreeSet<>(walker.getInNotIgnored()).toString());
+ assertEquals("[id, onlyOut, text, value]", new TreeSet<>(walker.getOutNotIgnored()).toString());
+ }
+
+ /**
+ * Walker subclass that records items that are examined.
+ */
+ private static class MyWalker extends ClassWalker {
+ private List<String> classes = new ArrayList<>();
+ private List<String> methods = new ArrayList<>();
+
+ @Override
+ protected void examine(Class<?> clazz) {
+ classes.add(clazz.getSimpleName());
+
+ super.examine(clazz);
+ }
+
+ @Override
+ protected void examine(Method method) {
+ if (Adapter.isManaged(method)) {
+ methods.add(method.getName());
+ }
+
+ super.examine(method);
+ }
+ }
+
+ protected static interface Intfc1 {
+ int id = 1000;
+ }
+
+ protected static interface Intfc2 {
+ String text = "intfc2-text";
+ }
+
+ private static interface Intfc3 {
+
+ }
+
+ protected static class Bottom implements Intfc1, Intfc3 {
+ private int id;
+ public String value;
+
+ public String invalid$fieldName;
+
+ @GsonJsonProperty("exposed")
+ private String exposedField;
+
+ @GsonJsonIgnore
+ public int ignored;
+
+ public transient int ignoredTransField;
+
+ @GsonJsonProperty("trans")
+ public transient int transField;
+
+ @GsonJsonIgnore
+ public int getId() {
+ return id;
+ }
+
+ @GsonJsonIgnore
+ public void setId(int id) {
+ this.id = id;
+ }
+ }
+
+ protected static class DerivedFromBottom extends Bottom implements Intfc1, Intfc2 {
+ private String text;
+ protected String anotherValue;
+
+ @GsonJsonProperty("value")
+ public String overriddenValue;
+
+ @GsonJsonIgnore
+ public String getText() {
+ return text;
+ }
+
+ @GsonJsonIgnore
+ public void setText(String text) {
+ this.text = text;
+ }
+ }
+
+ protected static class Data {
+ private int id;
+ private String text;
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ // not public, but property provided
+ @GsonJsonProperty("text")
+ protected String getText() {
+ return text;
+ }
+
+ // this will be ignored, because there's already a field by this name
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ // should only show up in the output list
+ public int getOnlyOut() {
+ return 1100;
+ }
+
+ // will be overridden by subclass
+ @GsonJsonProperty("super-value-getter")
+ public String getValue() {
+ return null;
+ }
+
+ // will be overridden by subclass
+ @GsonJsonProperty("super-value-setter")
+ public void setValue(String value) {
+ // do nothing
+ }
+ }
+
+ protected static class DerivedFromData extends Data {
+ // not serialized
+ private String unserialized;
+
+ // overrides private field and public method from Data
+ public String text;
+
+ private Map<String, String> map;
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ @GsonJsonAnyGetter
+ public Map<String, String> getTheMap() {
+ return map;
+ }
+
+ @GsonJsonIgnore
+ public void setMap(Map<String, String> map) {
+ this.map = map;
+ }
+
+ @GsonJsonAnySetter
+ public void setMapValue(String key, String value) {
+ if (map == null) {
+ map = new TreeMap<>();
+ }
+
+ map.put(key, value);
+ }
+
+ @GsonJsonIgnore
+ public String getUnserialized() {
+ return unserialized;
+ }
+
+ @GsonJsonIgnore
+ public void setUnserialized(String unserialized) {
+ this.unserialized = unserialized;
+ }
+
+ // should only show up in the input list
+ public void setOnlyIn(int value) {
+ // do nothing
+ }
+
+ // has a param - shouldn't be serialized
+ public int getWithParams(String text) {
+ return 1000;
+ }
+
+ // too few params - shouldn't be serialized
+ public void setMissingParams() {
+ // do nothing
+ }
+
+ // too many params - shouldn't be serialized
+ public void setExtraParams(String text, String moreText) {
+ // do nothing
+ }
+
+ // not public - shouldn't be serialized
+ protected void setNonPublic(String text) {
+ // do nothing
+ }
+
+ // doesn't start with "get"
+ public String wrongGetPrefix() {
+ return null;
+ }
+
+ // doesn't start with "set"
+ public void wrongSetPrefix(String text) {
+ // do nothing
+ }
+
+ // static
+ public static String getStatic() {
+ return null;
+ }
+ }
+
+ /**
+ * The "get" method has an incorrect argument count.
+ */
+ private static class AnyGetterMismatchParams {
+ @GsonJsonAnyGetter
+ public Map<String, String> getTheMap(String arg) {
+ return new TreeMap<>();
+ }
+ }
+
+ /**
+ * Has {@link GsonJsonAnyGetter} method.
+ */
+ private static class AnyGetterOnly {
+ @GsonJsonAnyGetter
+ private Map<String, Integer> getOverride() {
+ return null;
+ }
+ }
+
+ /**
+ * Has {@link GsonJsonAnyGetter} method, but it's ignored.
+ */
+ private static class AnyGetterIgnored {
+ @GsonJsonAnyGetter
+ @GsonJsonIgnore
+ private Map<String, Integer> getOverride() {
+ return null;
+ }
+ }
+
+ /**
+ * Has {@link GsonJsonAnySetter} method.
+ */
+ private static class AnySetterOnly {
+ @GsonJsonAnySetter
+ private void setOverride(String key, int value) {
+ // do nothing
+ }
+ }
+
+ /**
+ * Has {@link GsonJsonAnySetter} method, but it's ignored.
+ */
+ private static class AnySetterIgnored {
+ @GsonJsonAnySetter
+ @GsonJsonIgnore
+ private void setOverride(String key, int value) {
+ // do nothing
+ }
+ }
+
+ /**
+ * Has {@link GsonJsonAnyGetter} method that overrides the super class' method.
+ */
+ private static class AnyGetterOverride extends DerivedFromData {
+ private Map<String, Integer> overMap;
+
+ @GsonJsonAnyGetter
+ private Map<String, Integer> getOverride() {
+ return overMap;
+ }
+ }
+
+ /**
+ * Has {@link GsonJsonAnySetter} method that overrides the super class' method.
+ */
+ private static class AnySetterOverride extends DerivedFromData {
+ private Map<String, Integer> overMap;
+
+ @GsonJsonAnySetter
+ private void setOverride(String key, int value) {
+ if (overMap == null) {
+ overMap = new TreeMap<>();
+ }
+
+ overMap.put(key, value);
+ }
+ }
+
+ /**
+ * Has {@link GsonJsonAnySetter} method with too few parameters.
+ */
+ private static class AnySetterTooFewParams extends DerivedFromData {
+ @GsonJsonAnySetter
+ public void setOverride(String key) {
+ // do nothing
+ }
+ }
+
+ /**
+ * Has {@link GsonJsonAnySetter} method with too few parameters.
+ */
+ private static class AnySetterTooManyParams extends DerivedFromData {
+ @GsonJsonAnySetter
+ public void setOverride(String key, int value, String anotherValue) {
+ // do nothing
+ }
+ }
+
+ /**
+ * Has {@link GsonJsonAnySetter} method whose first argument type is incorrect.
+ */
+ private static class AnySetterInvalidParam extends DerivedFromData {
+ @GsonJsonAnySetter
+ public void setOverride(Integer key, String value) {
+ // do nothing
+ }
+ }
+}
diff --git a/gson/src/test/java/org/onap/policy/common/gson/internal/DataAdapterFactory.java b/gson/src/test/java/org/onap/policy/common/gson/internal/DataAdapterFactory.java
new file mode 100644
index 00000000..d0f0b1ec
--- /dev/null
+++ b/gson/src/test/java/org/onap/policy/common/gson/internal/DataAdapterFactory.java
@@ -0,0 +1,310 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.common.gson.internal;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Factory used with test Data.
+ */
+public class DataAdapterFactory implements TypeAdapterFactory {
+
+ /**
+ * Output of {@link #makeList()}, encoded as json.
+ */
+ public static final String ENCODED_LIST = "[{'id':100},{'id':101}]".replace('\'', '"');
+
+ /**
+ * Output of {@link #makeMap()}, encoded as json.
+ */
+ public static final String ENCODED_MAP = "'data-100':{'id':100},'data-101':{'id':101}".replace('\'', '"');
+
+ /**
+ * Object handled by this factory.
+ */
+ public static class Data {
+ private int id;
+
+ public Data() {
+ super();
+ }
+
+ public Data(int id) {
+ this.id = id;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ @Override
+ public String toString() {
+ return "Data [id=" + id + "]";
+ }
+ }
+
+ /**
+ * Object derived from Data.
+ */
+ public static class DerivedData extends Data {
+ private String text;
+
+ public DerivedData() {
+ super();
+ }
+
+ public DerivedData(int id, String text) {
+ super(id);
+ this.text = text;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ @Override
+ public String toString() {
+ return "DerivedData [text=" + text + ", toString()=" + super.toString() + "]";
+ }
+ }
+
+ /**
+ * Set to {@code true} when {@link #write(JsonWriter, Data)} has been invoked.
+ */
+ private boolean dataWritten = false;
+
+ /**
+ * Set to {@code true} when {@link #read(JsonReader)} has been invoked.
+ */
+ private boolean dataRead = false;
+
+ /**
+ * Clears the flags that indicate that "read" or "write" has been invoked.
+ */
+ public void reset() {
+ dataWritten = true;
+ dataRead = true;
+ }
+
+ public boolean isDataWritten() {
+ return dataWritten;
+ }
+
+ public boolean isDataRead() {
+ return dataRead;
+ }
+
+ /**
+ * Makes a list of Data.
+ *
+ * @return a new list of Data
+ */
+ public static List<Data> makeList() {
+ List<Data> listField = new ArrayList<>();
+
+ listField.add(new Data(100));
+ listField.add(new Data(101));
+
+ return listField;
+ }
+
+ /**
+ * Makes an array of Data.
+ *
+ * @return a new array of Data
+ */
+ public static JsonArray makeArray() {
+ JsonArray arr = new JsonArray();
+
+ for (Data data : makeList()) {
+ JsonObject json = new JsonObject();
+ json.addProperty("id", data.getId());
+ arr.add(json);
+ }
+
+ return arr;
+ }
+
+ /**
+ * Makes a map of Data.
+ *
+ * @return a new map of Data
+ */
+ public static Map<String, List<Data>> makeMap() {
+ Map<String, List<Data>> map = new TreeMap<>();
+
+ for (Data data : makeList()) {
+ map.put("data-" + data.getId(), Arrays.asList(data));
+ }
+
+ return map;
+ }
+
+ /**
+ * Adds Data objects to a tree, mirroring {@link #makeMap()}.
+ *
+ * @param tree tree into which objects are to be added
+ */
+ public static void addToObject(JsonObject tree) {
+ for (JsonElement ent : makeArray()) {
+ JsonObject obj = ent.getAsJsonObject();
+ JsonArray arr = new JsonArray();
+ arr.add(obj);
+ tree.add("data-" + obj.get("id").getAsString(), arr);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
+ if (type.getRawType() == Data.class) {
+ return (TypeAdapter<T>) new DataTypeAdapter(gson.getDelegateAdapter(this, TypeToken.get(Data.class)),
+ gson.getAdapter(JsonElement.class));
+ }
+
+ if (type.getRawType() == DerivedData.class) {
+ return (TypeAdapter<T>) new DerivedDataTypeAdapter(
+ gson.getDelegateAdapter(this, TypeToken.get(DerivedData.class)),
+ gson.getAdapter(JsonElement.class));
+ }
+
+ return null;
+ }
+
+ /**
+ * Adapter for "Data".
+ */
+ private class DataTypeAdapter extends TypeAdapter<Data> {
+ private TypeAdapter<Data> delegate;
+ private TypeAdapter<JsonElement> elementAdapter;
+
+ /**
+ * Constructs the object.
+ *
+ * @param delegate delegate adapter
+ * @param elementAdapter element adapter
+ */
+ public DataTypeAdapter(TypeAdapter<Data> delegate, TypeAdapter<JsonElement> elementAdapter) {
+ this.delegate = delegate;
+ this.elementAdapter = elementAdapter;
+ }
+
+ @Override
+ public void write(JsonWriter out, Data data) throws IOException {
+ dataWritten = true;
+
+ JsonElement tree = delegate.toJsonTree(data);
+
+ if (tree.isJsonObject()) {
+ JsonObject jsonObj = tree.getAsJsonObject();
+ jsonObj.addProperty("id", data.getId());
+ }
+
+ elementAdapter.write(out, tree);
+ }
+
+ @Override
+ public Data read(JsonReader in) throws IOException {
+ dataRead = true;
+
+ JsonElement tree = elementAdapter.read(in);
+ Data data = delegate.fromJsonTree(tree);
+
+ if (tree.isJsonObject()) {
+ JsonObject jsonObj = tree.getAsJsonObject();
+ data.setId(jsonObj.get("id").getAsInt());
+ }
+
+ return data;
+ }
+ }
+ /**
+ * Adapter for "DerivedData".
+ */
+ private class DerivedDataTypeAdapter extends TypeAdapter<DerivedData> {
+ private TypeAdapter<DerivedData> delegate;
+ private TypeAdapter<JsonElement> elementAdapter;
+
+ /**
+ * Constructs the object.
+ *
+ * @param delegate delegate adapter
+ * @param elementAdapter element adapter
+ */
+ public DerivedDataTypeAdapter(TypeAdapter<DerivedData> delegate, TypeAdapter<JsonElement> elementAdapter) {
+ this.delegate = delegate;
+ this.elementAdapter = elementAdapter;
+ }
+
+ @Override
+ public void write(JsonWriter out, DerivedData data) throws IOException {
+ dataWritten = true;
+
+ JsonElement tree = delegate.toJsonTree(data);
+
+ if (tree.isJsonObject()) {
+ JsonObject jsonObj = tree.getAsJsonObject();
+ jsonObj.addProperty("id", data.getId());
+ jsonObj.addProperty("text", data.getText());
+ }
+
+ elementAdapter.write(out, tree);
+ }
+
+ @Override
+ public DerivedData read(JsonReader in) throws IOException {
+ dataRead = true;
+
+ JsonElement tree = elementAdapter.read(in);
+ DerivedData data = delegate.fromJsonTree(tree);
+
+ if (tree.isJsonObject()) {
+ JsonObject jsonObj = tree.getAsJsonObject();
+ data.setId(jsonObj.get("id").getAsInt());
+ data.setText(jsonObj.get("text").getAsString());
+ }
+
+ return data;
+ }
+ }
+}