Add enhancements to standard coder 26/101426/3
authorJim Hahn <jrh3@att.com>
Sat, 8 Feb 2020 18:02:02 +0000 (13:02 -0500)
committerJim Hahn <jrh3@att.com>
Sun, 9 Feb 2020 04:39:27 +0000 (23:39 -0500)
Added support for array indices in StandardCoderObject getString().
Also made it Serializable.

Issue-ID: POLICY-1625
Signed-off-by: Jim Hahn <jrh3@att.com>
Change-Id: Ia514aed96fdfe7f635c5a6dc3e1f90939654d383

utils/src/main/java/org/onap/policy/common/utils/coder/StandardCoderObject.java
utils/src/test/java/org/onap/policy/common/utils/coder/StandardCoderObjectTest.java

index 60c5f4e..7f0f058 100644 (file)
@@ -2,7 +2,7 @@
  * ============LICENSE_START=======================================================
  * ONAP
  * ================================================================================
- * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * Copyright (C) 2019-2020 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.
 
 package org.onap.policy.common.utils.coder;
 
+import com.google.gson.JsonArray;
 import com.google.gson.JsonElement;
+import java.io.Serializable;
 
 /**
  * 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 {
+public class StandardCoderObject implements Serializable {
+    private static final long serialVersionUID = 1L;
 
     /**
      * Data wrapped by this.
@@ -62,32 +65,73 @@ public class StandardCoderObject {
     /**
      * Gets a field's value from this object, traversing the object hierarchy.
      *
-     * @param fields field hierarchy
+     * @param fields field hierarchy. These may be strings, identifying fields within the
+     *        object, or Integers, identifying an index within an array
      * @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.
-         */
+    public String getString(Object... fields) {
 
         JsonElement jel = data;
 
-        for (String field : fields) {
+        for (Object field : fields) {
             if (jel == null) {
                 return null;
             }
 
-            if (jel.isJsonObject()) {
-                jel = jel.getAsJsonObject().get(field);
+            if (field instanceof String) {
+                jel = getFieldFromObject(jel, field.toString());
+
+            } else if (field instanceof Integer) {
+                jel = getItemFromArray(jel, (int) field);
 
             } else {
-                return null;
+                throw new IllegalArgumentException("subscript is not a string or integer: " + field);
             }
         }
 
         return (jel != null && jel.isJsonPrimitive() ? jel.getAsString() : null);
     }
+
+    /**
+     * Gets an item from an object.
+     *
+     * @param element object from which to extract the item
+     * @param field name of the field from which to extract the item
+     * @return the item, or {@code null} if the element is not an object or if the field
+     *         does not exist
+     */
+    protected JsonElement getFieldFromObject(JsonElement element, String field) {
+        if (!element.isJsonObject()) {
+            return null;
+        }
+
+        return element.getAsJsonObject().get(field);
+    }
+
+    /**
+     * Gets an item from an array.
+     *
+     * @param element array from which to extract the item
+     * @param index index of the item to extract
+     * @return the item, or {@code null} if the element is not an array or if the index is
+     *         out of bounds
+     */
+    protected JsonElement getItemFromArray(JsonElement element, int index) {
+        if (index < 0) {
+            throw new IllegalArgumentException("subscript is invalid: " + index);
+        }
+
+        if (!element.isJsonArray()) {
+            return null;
+        }
+
+        JsonArray array = element.getAsJsonArray();
+
+        if (index >= array.size()) {
+            return null;
+        }
+
+        return array.get(index);
+    }
 }
index 44086f3..1748aed 100644 (file)
@@ -2,7 +2,7 @@
  * ============LICENSE_START=======================================================
  * ONAP
  * ================================================================================
- * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * Copyright (C) 2019-2020 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.
@@ -20,6 +20,7 @@
 
 package org.onap.policy.common.utils.coder;
 
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
@@ -34,10 +35,11 @@ public class StandardCoderObjectTest {
 
     private static final String PROP1 = "abc";
     private static final String PROP2 = "ghi";
+    private static final Integer PROP2_INDEX = 1;
     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 static final String JSON = "{'abc':'def','ghi':[{},{'jkl':'mno'}]}".replace('\'', '"');
 
     private StandardCoderObject sco;
 
@@ -68,7 +70,7 @@ public class StandardCoderObjectTest {
         assertEquals(VAL1, sco.getString(PROP1));
 
         // multiple fields
-        assertEquals(VAL2, sco.getString(PROP2, PROP2b));
+        assertEquals(VAL2, sco.getString(PROP2, PROP2_INDEX, PROP2b));
 
         // not found
         assertNull(sco.getString("xyz"));
@@ -85,5 +87,44 @@ public class StandardCoderObjectTest {
 
         // not a JSON object
         assertNull(sco.getString(PROP1, PROP2));
+
+        // invalid subscript
+        assertThatIllegalArgumentException().isThrownBy(() -> sco.getString(10.0));
+    }
+
+    @Test
+    public void testGetFieldFromObject() {
+        // not an object
+        assertNull(sco.getFieldFromObject(fromJson("[]"), PROP1));
+
+        // field doesn't exist
+        assertNull(sco.getFieldFromObject(fromJson("{}"), "non-existent"));
+
+        // field exists
+        assertEquals(4, sco.getFieldFromObject(fromJson("{\"world\":4}"), "world").getAsInt());
+    }
+
+    @Test
+    public void testGetItemFromArray() {
+        // not an array
+        assertNull(sco.getItemFromArray(fromJson("{}"), 0));
+
+        // negative index
+        assertThatIllegalArgumentException().isThrownBy(() -> sco.getItemFromArray(fromJson("[]"), -1));
+
+        // index out of bounds
+        assertNull(sco.getItemFromArray(fromJson("[5]"), 1));
+        assertNull(sco.getItemFromArray(fromJson("[5]"), 2));
+
+        // index exists
+        assertEquals(6, sco.getItemFromArray(fromJson("[5,6,7]"), 1).getAsInt());
+
+        // edge case: first and last item
+        assertEquals(50, sco.getItemFromArray(fromJson("[50,60,70]"), 0).getAsInt());
+        assertEquals(700, sco.getItemFromArray(fromJson("[500,600,700]"), 2).getAsInt());
+    }
+
+    private JsonElement fromJson(String json) {
+        return gson.fromJson(json, JsonElement.class);
     }
 }