diff options
5 files changed, 303 insertions, 1 deletions
diff --git a/utils/src/main/java/org/onap/policy/common/utils/coder/Coder.java b/utils/src/main/java/org/onap/policy/common/utils/coder/Coder.java index 66a308f7..bb51f2b9 100644 --- a/utils/src/main/java/org/onap/policy/common/utils/coder/Coder.java +++ b/utils/src/main/java/org/onap/policy/common/utils/coder/Coder.java @@ -106,4 +106,22 @@ public interface Coder { * @throws CoderException if an error occurs */ <T> T decode(File source, Class<T> clazz) throws CoderException; + + /** + * Converts an object/POJO to a standard object. + * + * @param object object to be converted + * @return a new standard object representing the original object + * @throws CoderException if an error occurs + */ + StandardCoderObject toStandard(Object object) throws CoderException; + + /** + * Converts a standard object to an object/POJO. + * + * @param sco the standard object to be converted + * @return a new object represented by the standard object + * @throws CoderException if an error occurs + */ + <T> T fromStandard(StandardCoderObject sco, Class<T> clazz) throws CoderException; } diff --git a/utils/src/main/java/org/onap/policy/common/utils/coder/StandardCoder.java b/utils/src/main/java/org/onap/policy/common/utils/coder/StandardCoder.java index 389720f9..69a211b6 100644 --- a/utils/src/main/java/org/onap/policy/common/utils/coder/StandardCoder.java +++ b/utils/src/main/java/org/onap/policy/common/utils/coder/StandardCoder.java @@ -21,6 +21,11 @@ package org.onap.policy.common.utils.coder; import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -42,7 +47,8 @@ public class StandardCoder implements Coder { /** * Gson object used to encode and decode messages. */ - private static final Gson GSON = new Gson(); + private static final Gson GSON = new GsonBuilder() + .registerTypeAdapter(StandardCoderObject.class, new StandardTypeAdapter()).create(); /** * Constructs the object. @@ -137,6 +143,26 @@ public class StandardCoder implements Coder { } } + @Override + public StandardCoderObject toStandard(Object object) throws CoderException { + try { + return new StandardCoderObject(GSON.toJsonTree(object)); + + } catch (RuntimeException e) { + throw new CoderException(e); + } + } + + @Override + public <T> T fromStandard(StandardCoderObject sco, Class<T> clazz) throws CoderException { + try { + return GSON.fromJson(sco.getData(), clazz); + + } catch (RuntimeException e) { + throw new CoderException(e); + } + } + // the remaining methods are wrappers that can be overridden by junit tests /** @@ -223,4 +249,32 @@ public class StandardCoder implements Coder { protected <T> T fromJson(Reader source, Class<T> clazz) { return GSON.fromJson(source, clazz); } + + /** + * Adapter for standard objects. + */ + private static class StandardTypeAdapter extends TypeAdapter<StandardCoderObject> { + + /** + * Used to read/write a JsonElement. + */ + private static TypeAdapter<JsonElement> elementAdapter = new Gson().getAdapter(JsonElement.class); + + /** + * Constructs the object. + */ + public StandardTypeAdapter() { + super(); + } + + @Override + public void write(JsonWriter out, StandardCoderObject value) throws IOException { + elementAdapter.write(out, value.getData()); + } + + @Override + public StandardCoderObject read(JsonReader in) throws IOException { + return new StandardCoderObject(elementAdapter.read(in)); + } + } } diff --git a/utils/src/main/java/org/onap/policy/common/utils/coder/StandardCoderObject.java b/utils/src/main/java/org/onap/policy/common/utils/coder/StandardCoderObject.java new file mode 100644 index 00000000..60c5f4ef --- /dev/null +++ b/utils/src/main/java/org/onap/policy/common/utils/coder/StandardCoderObject.java @@ -0,0 +1,93 @@ +/* + * ============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.utils.coder; + +import com.google.gson.JsonElement; + +/** + * Object type used by the {@link StandardCoder}. Different serialization tools have + * different "standard objects". For instance, GSON uses {@link JsonElement}. This class + * wraps that object so that it can be used without exposing the object, itself. + */ +public class StandardCoderObject { + + /** + * Data wrapped by this. + */ + private final JsonElement data; + + /** + * Constructs the object. + */ + public StandardCoderObject() { + data = null; + } + + /** + * Constructs the object. + * + * @param data data wrapped by this object. + */ + protected StandardCoderObject(JsonElement data) { + this.data = data; + } + + /** + * Gets the data wrapped by this. + * + * @return the data wrapped by this + */ + protected JsonElement getData() { + return data; + } + + /** + * Gets a field's value from this object, traversing the object hierarchy. + * + * @param fields field hierarchy + * @return the field value or {@code null} if the field does not exist or is not a + * primitive + */ + public String getString(String... fields) { + + /* + * This could be relatively easily modified to allow Integer arguments, as well, + * which would be used to specify indices within an array. + */ + + JsonElement jel = data; + + for (String field : fields) { + if (jel == null) { + return null; + } + + if (jel.isJsonObject()) { + jel = jel.getAsJsonObject().get(field); + + } else { + return null; + } + } + + return (jel != null && jel.isJsonPrimitive() ? jel.getAsString() : null); + } +} diff --git a/utils/src/test/java/org/onap/policy/common/utils/coder/StandardCoderObjectTest.java b/utils/src/test/java/org/onap/policy/common/utils/coder/StandardCoderObjectTest.java new file mode 100644 index 00000000..44086f30 --- /dev/null +++ b/utils/src/test/java/org/onap/policy/common/utils/coder/StandardCoderObjectTest.java @@ -0,0 +1,89 @@ +/* + * ============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.utils.coder; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import org.junit.Before; +import org.junit.Test; + +public class StandardCoderObjectTest { + private static final Gson gson = new Gson(); + + private static final String PROP1 = "abc"; + private static final String PROP2 = "ghi"; + private static final String PROP2b = "jkl"; + private static final String VAL1 = "def"; + private static final String VAL2 = "mno"; + private static final String JSON = "{'abc':'def','ghi':{'jkl':'mno'}}".replace('\'', '"'); + + private StandardCoderObject sco; + + /** + * Creates a standard object, populated with some data. + * + * @throws Exception if an error occurs + */ + @Before + public void setUp() throws Exception { + sco = new StandardCoderObject(gson.fromJson(JSON, JsonElement.class)); + } + + @Test + public void testStandardCoderObject() { + assertNull(new StandardCoderObject().getData()); + } + + @Test + public void testStandardCoderObjectJsonElement() { + assertNotNull(sco.getData()); + assertEquals(JSON, gson.toJson(sco.getData())); + } + + @Test + public void testGetString() throws Exception { + // one field + assertEquals(VAL1, sco.getString(PROP1)); + + // multiple fields + assertEquals(VAL2, sco.getString(PROP2, PROP2b)); + + // not found + assertNull(sco.getString("xyz")); + + // read from null object + assertNull(new StandardCoderObject().getString()); + assertNull(new StandardCoderObject().getString(PROP1)); + + JsonElement obj = gson.fromJson("{'abc':[]}".replace('\'', '"'), JsonElement.class); + sco = new StandardCoderObject(obj); + + // not a primitive + assertNull(sco.getString(PROP1)); + + // not a JSON object + assertNull(sco.getString(PROP1, PROP2)); + } +} diff --git a/utils/src/test/java/org/onap/policy/common/utils/coder/StandardCoderTest.java b/utils/src/test/java/org/onap/policy/common/utils/coder/StandardCoderTest.java index 25cce748..7583d776 100644 --- a/utils/src/test/java/org/onap/policy/common/utils/coder/StandardCoderTest.java +++ b/utils/src/test/java/org/onap/policy/common/utils/coder/StandardCoderTest.java @@ -22,6 +22,7 @@ package org.onap.policy.common.utils.coder; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @@ -196,4 +197,51 @@ public class StandardCoderTest { assertThatThrownBy(() -> coder.decode(file, JsonElement.class)).isInstanceOf(CoderException.class) .hasCause(ioe); } + + @Test + public void testToStandard() throws Exception { + MyObject obj = new MyObject(); + obj.abc = "xyz"; + StandardCoderObject sco = coder.toStandard(obj); + assertNotNull(sco.getData()); + assertEquals("{'abc':'xyz'}".replace('\'', '"'), sco.getData().toString()); + + // class instead of object -> exception + assertThatThrownBy(() -> coder.toStandard(String.class)).isInstanceOf(CoderException.class); + } + + @Test + public void testFromStandard() throws Exception { + MyObject obj = new MyObject(); + obj.abc = "pdq"; + StandardCoderObject sco = coder.toStandard(obj); + + MyObject obj2 = coder.fromStandard(sco, MyObject.class); + assertEquals(obj.toString(), obj2.toString()); + + // null class -> exception + assertThatThrownBy(() -> coder.fromStandard(sco, null)).isInstanceOf(CoderException.class); + } + + @Test + public void testStandardTypeAdapter() throws Exception { + String json = "{'abc':'def'}".replace('\'', '"'); + StandardCoderObject sco = coder.fromJson(json, StandardCoderObject.class); + assertNotNull(sco.getData()); + assertEquals(json, sco.getData().toString()); + assertEquals(json, coder.toJson(sco)); + + // invalid json -> exception + assertThatThrownBy(() -> coder.fromJson(new StringReader("["), StandardCoderObject.class)); + } + + + private static class MyObject { + private String abc; + + @Override + public String toString() { + return "MyObject [abc=" + abc + "]"; + } + } } |