Supporting workflow list & map json data model for workflow context.
Workflow json data model bind to list & map objects
usage. For example
List<Object> data;
Map<Object, Object> data.
Change-Id: I46899c8d2267ebca0663e85302eba2242fbcfdda
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/DataModelTree.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/DataModelTree.java
index f7ada8d..9c54535 100644
--- a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/DataModelTree.java
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/DataModelTree.java
@@ -61,10 +61,18 @@
DataModelTree alloc(String path, Nodetype leaftype) throws WorkflowException;
/**
- * Remove node on the path.
+ * Remove node on the path. This removes the entity.
* @param path data model tree path
* @throws WorkflowException workflow exception
*/
void remove(String path) throws WorkflowException;
+
+ /**
+ * Clear node on the path. This does not remove the entry. This initializes the value to Null.
+ * @param path data model tree path
+ * @throws WorkflowException workflow exception
+ */
+ void clear(String path) throws WorkflowException;
+
}
diff --git a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/JsonDataModelTree.java b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/JsonDataModelTree.java
index aec19ae..8a0ec84 100644
--- a/apps/workflow/api/src/main/java/org/onosproject/workflow/api/JsonDataModelTree.java
+++ b/apps/workflow/api/src/main/java/org/onosproject/workflow/api/JsonDataModelTree.java
@@ -18,14 +18,17 @@
import com.fasterxml.jackson.core.JsonPointer;
import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.BooleanNode;
import com.fasterxml.jackson.databind.node.IntNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.JsonNodeType;
import com.fasterxml.jackson.databind.node.MissingNode;
+import com.fasterxml.jackson.databind.node.NullNode;
import com.fasterxml.jackson.databind.node.NumericNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
@@ -33,6 +36,10 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import java.util.Objects;
/**
@@ -92,11 +99,11 @@
private void attach(JsonPointer ptr, JsonNode attachingNode) throws WorkflowException {
JsonNode node = root.at(ptr);
- if (!(node instanceof MissingNode)) {
+ if (!(node instanceof MissingNode || node instanceof NullNode)) {
throw new WorkflowException("Path(" + ptr + ") has already subtree(" + node + ")");
}
- if (ptr.last().getMatchingIndex() != -1) {
+ if (isArrayIndex(ptr.last())) {
alloc(ptr.head(), Nodetype.ARRAY);
JsonNode parentNode = root.at(ptr.head());
@@ -104,9 +111,14 @@
throw new WorkflowException("Invalid parentNode type(" + parentNode.getNodeType() + " != Array)");
}
int index = ptr.last().getMatchingIndex();
+
+ if (parentNode.get(index) instanceof NullNode) {
+ ((ArrayNode) parentNode).remove(index);
+ }
+
((ArrayNode) parentNode).insert(index, attachingNode);
- } else if (ptr.last().getMatchingProperty() != null) {
+ } else if (isMapKey(ptr.last())) {
alloc(ptr.head(), Nodetype.MAP);
JsonNode parentNode = root.at(ptr.head());
@@ -124,10 +136,16 @@
@Override
public void remove(String path) throws WorkflowException {
JsonPointer ptr = JsonPointer.compile(path);
- remove(ptr);
+ clear(ptr, true);
}
- private void remove(JsonPointer ptr) throws WorkflowException {
+ @Override
+ public void clear(String path) throws WorkflowException {
+ JsonPointer ptr = JsonPointer.compile(path);
+ clear(ptr, false);
+ }
+
+ private void clear(JsonPointer ptr, boolean dispose) throws WorkflowException {
JsonNode node = root.at(ptr);
if (node instanceof MissingNode) {
@@ -135,23 +153,31 @@
return;
}
- if (ptr.last().getMatchingIndex() != -1) {
+ if (isArrayIndex(ptr.last())) {
JsonNode parentNode = root.at(ptr.head());
if (!parentNode.isArray()) {
throw new WorkflowException("Invalid parentNode type(" + parentNode.getNodeType() + " != Array)");
}
int index = ptr.last().getMatchingIndex();
- ((ArrayNode) parentNode).remove(index);
+ if (dispose) {
+ ((ArrayNode) parentNode).remove(index);
+ } else {
+ ((ArrayNode) parentNode).set(index, NullNode.getInstance());
+ }
- } else if (ptr.last().getMatchingProperty() != null) {
+ } else if (isMapKey(ptr.last())) {
JsonNode parentNode = root.at(ptr.head());
if (!parentNode.isObject()) {
throw new WorkflowException("Invalid parentNode type(" + parentNode.getNodeType() + " != Object)");
}
String key = ptr.last().getMatchingProperty();
- ((ObjectNode) parentNode).remove(key);
+ if (dispose) {
+ ((ObjectNode) parentNode).remove(key);
+ } else {
+ ((ObjectNode) parentNode).putNull(key);
+ }
} else {
throw new WorkflowException("Invalid path(" + ptr + ")");
@@ -363,6 +389,35 @@
}
/**
+ * Gets list on specific path.
+ *
+ * @param path path of json node
+ * @param <T> class type
+ * @param listDataTypeClass data type class of list
+ * @return list on specific path
+ * @throws WorkflowException workflow exception
+ */
+ public <T> List<T> listAt(String path, Class<T> listDataTypeClass) throws WorkflowException {
+ JsonPointer ptr = JsonPointer.compile(path);
+ return listAt(ptr, listDataTypeClass);
+ }
+
+ /**
+ * Gets map on specific path.
+ *
+ * @param path path of json node
+ * @param <T> class type
+ * @param mapDataTypeClass data type of map value
+ * @return map of specific class type
+ * @throws WorkflowException workflow excepion
+ */
+ public <T> Map<String, T> mapAt(String path, Class<T> mapDataTypeClass) throws WorkflowException {
+ JsonPointer ptr = JsonPointer.compile(path);
+ return mapAt(ptr, mapDataTypeClass);
+ }
+
+
+ /**
* Gets integer on specific json pointer.
*
* @param ptr json pointer
@@ -380,7 +435,61 @@
if (!(node instanceof NumericNode)) {
throw new WorkflowException("Invalid node(" + node + ") at " + ptr);
}
- return ((NumericNode) node).asInt();
+ return node.asInt();
+ }
+
+ /**
+ * Gets list on specific json pointer.
+ *
+ * @param ptr json pointer
+ * @param <T> class type
+ * @param listDataTypeClass data type class of list
+ * @return list on specific json pointer
+ * @throws WorkflowException workflow exception
+ */
+ public <T> List<T> listAt(JsonPointer ptr, Class<T> listDataTypeClass) {
+ List<T> list = new ArrayList<>();
+ try {
+ if (root == null || root instanceof MissingNode) {
+ throw new WorkflowException("Invalid root node");
+ }
+ JsonNode node = root.at(ptr);
+ ObjectMapper objectMapper = new ObjectMapper();
+ ObjectReader reader = objectMapper.readerFor(new TypeReference<List<T>>() {
+ });
+ if (node.isMissingNode()) {
+ return null;
+ }
+ list = reader.readValue(node);
+ } catch (Exception e) {
+ log.info("exception while parsing list {}", e.getStackTrace());
+ }
+ return list;
+ }
+
+ /**
+ * Gets map on specific json pointer.
+ *
+ * @param ptr json pointer
+ * @param <T> class type
+ * @param mapDataTypeClass data type class of value in the map
+ * @return map on specific json pointer
+ */
+ public <T> Map<String, T> mapAt(JsonPointer ptr, Class<T> mapDataTypeClass) {
+ Map<String, T> map = new HashMap<>();
+ try {
+ if (root == null || root instanceof MissingNode) {
+ throw new WorkflowException("Invalid root node");
+ }
+ JsonNode node = root.at(ptr);
+ ObjectMapper objectMapper = new ObjectMapper();
+ ObjectReader reader = objectMapper.readerFor(new TypeReference<Map<String, T>>() {
+ });
+ map = reader.readValue(node);
+ } catch (Exception e) {
+ log.info("exception while parsing map {}", e.getStackTrace());
+ }
+ return map;
}
/**
@@ -552,6 +661,57 @@
}
/**
+ * Sets list on specific json path.
+ *
+ * @param path json path
+ * @param list list to set
+ * @throws WorkflowException workflow exception
+ */
+ public void setAt(String path, List list) throws WorkflowException {
+ JsonPointer ptr = JsonPointer.compile(path);
+ setAt(ptr, list);
+ }
+
+ /**
+ * Sets list on the specific json pointer.
+ *
+ * @param ptr json pointer
+ * @param list list to set
+ * @throws WorkflowException workflow exception
+ */
+ public void setAt(JsonPointer ptr, List list) throws WorkflowException {
+ ObjectMapper mapper = new ObjectMapper();
+ JsonNode node = mapper.valueToTree(list);
+ attach(ptr, node);
+ }
+
+ /**
+ * Sets map on specific json path.
+ *
+ * @param path json path
+ * @param map map to set
+ * @throws WorkflowException workflow exception
+ */
+ public void setAt(String path, Map map) throws WorkflowException {
+ JsonPointer ptr = JsonPointer.compile(path);
+ setAt(ptr, map);
+ }
+
+ /**
+ * Sets map on the specific json pointer.
+ *
+ * @param ptr json pointer
+ * @param map map to set
+ * @throws WorkflowException workflow exception
+ */
+ public void setAt(JsonPointer ptr, Map map) throws WorkflowException {
+ ObjectMapper mapper = new ObjectMapper();
+ JsonNode node = mapper.valueToTree(map);
+ attach(ptr, node);
+ }
+
+
+ /**
* Sets integer on the specific json pointer.
*
* @param ptr json pointer
@@ -574,8 +734,8 @@
*/
private JsonNode alloc(JsonNode node, JsonPointer ptr, JsonNodeType leaftype) throws WorkflowException {
- if (ptr.matches()) {
- if (node == null || node instanceof MissingNode) {
+ if (ptr.matches()) { // node is leaf
+ if (node == null || node instanceof MissingNode || node instanceof NullNode) {
node = createEmpty(leaftype);
} else {
//TODO: checking existing node type is matched with leaftype
@@ -587,19 +747,27 @@
return node;
}
- if (ptr.getMatchingIndex() != -1) {
- if (node == null || node instanceof MissingNode) {
+ // node is intermediate in the ptr
+ if (isArrayIndex(ptr)) {
+ if (node == null || node instanceof MissingNode || node instanceof NullNode) {
node = createEmpty(JsonNodeType.ARRAY);
}
JsonNode child = alloc(node.get(ptr.getMatchingIndex()), ptr.tail(), leaftype);
if (!node.has(ptr.getMatchingIndex())) {
((ArrayNode) node).insert(ptr.getMatchingIndex(), child);
+ } else {
+ ((ArrayNode) node).set(ptr.getMatchingIndex(), child);
}
- } else if (ptr.getMatchingProperty() != null) {
- if (node == null || node instanceof MissingNode) {
+ } else if (isMapKey(ptr)) {
+ if (node == null || node instanceof MissingNode || node instanceof NullNode) {
node = createEmpty(JsonNodeType.OBJECT);
}
JsonNode child = alloc(node.get(ptr.getMatchingProperty()), ptr.tail(), leaftype);
+
+ if (node.get(ptr.getMatchingProperty()) instanceof NullNode) {
+ ((ObjectNode) node).remove(ptr.getMatchingProperty());
+ }
+
if (!node.has(ptr.getMatchingProperty())) {
((ObjectNode) node).put(ptr.getMatchingProperty(), child);
}
@@ -641,6 +809,24 @@
return str;
}
+ /**
+ * Returns whether the json ptr is array type(having array index) or not.
+ * @param ptr
+ * @return Whether the json ptr is array type(having array index) or not.
+ */
+ private boolean isArrayIndex(JsonPointer ptr) {
+ return (ptr.getMatchingIndex() != -1);
+ }
+
+ /**
+ * Returns whether the json ptr is map type(having key) or not.
+ * @param ptr
+ * @return Whether the json ptr is map type(having key) or not.
+ */
+ private boolean isMapKey(JsonPointer ptr) {
+ return (ptr.getMatchingProperty() != null);
+ }
+
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())