From: Ed Warnicke Date: Tue, 26 Nov 2013 14:03:22 +0000 (+0000) Subject: Merge changes I50218f21,I6bc2631b X-Git-Tag: jenkins-controller-bulk-release-prepare-only-2-1~345 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=3979e330c9f95a898c54a9234f3a07e3b2ae4349;hp=f062dc05cc7caaf0c1811856370f1c9e2f1e5c34 Merge changes I50218f21,I6bc2631b * changes: Serialization to Json improvement Union type processing added to JsonMapper --- diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonMapper.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonMapper.java index 5a1b42fd80..073b24e033 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonMapper.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonMapper.java @@ -3,70 +3,58 @@ package org.opendaylight.controller.sal.rest.impl; import static com.google.common.base.Preconditions.checkNotNull; import java.io.IOException; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; import javax.activation.UnsupportedDataTypeException; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.api.Node; import org.opendaylight.yangtools.yang.data.api.SimpleNode; -import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; -import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; -import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; -import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode; -import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; -import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; -import org.opendaylight.yangtools.yang.model.api.TypeDefinition; -import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition; -import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition; -import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition; -import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition; -import org.opendaylight.yangtools.yang.model.api.type.IntegerTypeDefinition; -import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinition; +import org.opendaylight.yangtools.yang.model.api.*; +import org.opendaylight.yangtools.yang.model.api.type.*; import com.google.common.base.Preconditions; import com.google.gson.stream.JsonWriter; class JsonMapper { - + private final Set foundLeafLists = new HashSet<>(); private final Set foundLists = new HashSet<>(); - + public void write(JsonWriter writer, CompositeNode data, DataNodeContainer schema) throws IOException { Preconditions.checkNotNull(writer); Preconditions.checkNotNull(data); Preconditions.checkNotNull(schema); writer.beginObject(); - + if (schema instanceof ContainerSchemaNode) { writeContainer(writer, data, (ContainerSchemaNode) schema); } else if (schema instanceof ListSchemaNode) { - writeList(writer, data, (ListSchemaNode) schema); + writeList(writer, null, data, (ListSchemaNode) schema); } else { throw new UnsupportedDataTypeException( "Schema can be ContainerSchemaNode or ListSchemaNode. Other types are not supported yet."); } - + writer.endObject(); - + foundLeafLists.clear(); foundLists.clear(); } - private void writeChildrenOfParent(JsonWriter writer, CompositeNode parent, DataNodeContainer parentSchema) throws IOException { + private void writeChildrenOfParent(JsonWriter writer, CompositeNode parent, DataNodeContainer parentSchema) + throws IOException { checkNotNull(parent); checkNotNull(parentSchema); - + for (Node child : parent.getChildren()) { DataSchemaNode childSchema = findFirstSchemaForNode(child, parentSchema.getChildNodes()); if (childSchema == null) { throw new UnsupportedDataTypeException("Probably the data node \"" + child.getNodeType().getLocalName() + "\" is not conform to schema"); } - + if (childSchema instanceof ContainerSchemaNode) { Preconditions.checkState(child instanceof CompositeNode, "Data representation of Container should be CompositeNode - " + child.getNodeType()); @@ -76,14 +64,14 @@ class JsonMapper { Preconditions.checkState(child instanceof CompositeNode, "Data representation of List should be CompositeNode - " + child.getNodeType()); foundLists.add((ListSchemaNode) childSchema); - writeList(writer, (CompositeNode) child, (ListSchemaNode) childSchema); + writeList(writer, parent, (CompositeNode) child, (ListSchemaNode) childSchema); } } else if (childSchema instanceof LeafListSchemaNode) { if (!foundLeafLists.contains(childSchema)) { Preconditions.checkState(child instanceof SimpleNode, "Data representation of LeafList should be SimpleNode - " + child.getNodeType()); foundLeafLists.add((LeafListSchemaNode) childSchema); - writeLeafList(writer, (SimpleNode) child, (LeafListSchemaNode) childSchema); + writeLeafList(writer, parent, (SimpleNode) child, (LeafListSchemaNode) childSchema); } } else if (childSchema instanceof LeafSchemaNode) { Preconditions.checkState(child instanceof SimpleNode, @@ -94,7 +82,7 @@ class JsonMapper { + "LeafListSchemaNode, or LeafSchemaNode. Other types are not supported yet."); } } - + for (Node child : parent.getChildren()) { DataSchemaNode childSchema = findFirstSchemaForNode(child, parentSchema.getChildNodes()); if (childSchema instanceof LeafListSchemaNode) { @@ -104,7 +92,7 @@ class JsonMapper { } } } - + private DataSchemaNode findFirstSchemaForNode(Node node, Set dataSchemaNode) { for (DataSchemaNode dsn : dataSchemaNode) { if (node.getNodeType().getLocalName().equals(dsn.getQName().getLocalName())) { @@ -113,84 +101,154 @@ class JsonMapper { } return null; } - + private void writeContainer(JsonWriter writer, CompositeNode node, ContainerSchemaNode schema) throws IOException { writer.name(node.getNodeType().getLocalName()); writer.beginObject(); writeChildrenOfParent(writer, node, schema); writer.endObject(); } - - private void writeList(JsonWriter writer, CompositeNode node, ListSchemaNode schema) throws IOException { - writer.name(node.getNodeType().getLocalName()); - writer.beginArray(); - - if (node.getParent() != null) { - CompositeNode parent = node.getParent(); - List nodeLists = parent.getCompositesByName(node.getNodeType()); - for (CompositeNode nodeList : nodeLists) { - writer.beginObject(); - writeChildrenOfParent(writer, nodeList, schema); - writer.endObject(); - } - } else { + + private void writeList(JsonWriter writer, CompositeNode nodeParent, CompositeNode node, ListSchemaNode schema) throws IOException { + writer.name(node.getNodeType().getLocalName()); + writer.beginArray(); + + if (nodeParent != null) { + List nodeLists = nodeParent.getCompositesByName(node.getNodeType()); + for (CompositeNode nodeList : nodeLists) { writer.beginObject(); - writeChildrenOfParent(writer, node, schema); + writeChildrenOfParent(writer, nodeList, schema); writer.endObject(); } - - writer.endArray(); - } - - private void writeLeafList(JsonWriter writer, SimpleNode node, LeafListSchemaNode schema) throws IOException { - writer.name(node.getNodeType().getLocalName()); - writer.beginArray(); - - CompositeNode parent = node.getParent(); - List> nodeLeafLists = parent.getSimpleNodesByName(node.getNodeType()); - for (SimpleNode nodeLeafList : nodeLeafLists) { - writeValueOfNodeByType(writer, nodeLeafList, schema.getType()); - } - - writer.endArray(); + } else { + writer.beginObject(); + writeChildrenOfParent(writer, node, schema); + writer.endObject(); + } + + writer.endArray(); } - + + private void writeLeafList(JsonWriter writer, CompositeNode nodeParent, SimpleNode node, LeafListSchemaNode schema) throws IOException { + writer.name(node.getNodeType().getLocalName()); + writer.beginArray(); + + List> nodeLeafLists = nodeParent.getSimpleNodesByName(node.getNodeType()); + for (SimpleNode nodeLeafList : nodeLeafLists) { + writeValueOfNodeByType(writer, nodeLeafList, schema.getType()); + } + + writer.endArray(); + } + private void writeLeaf(JsonWriter writer, SimpleNode node, LeafSchemaNode schema) throws IOException { writer.name(node.getNodeType().getLocalName()); writeValueOfNodeByType(writer, node, schema.getType()); } - - private void writeValueOfNodeByType(JsonWriter writer, SimpleNode node, TypeDefinition type) throws IOException { + + private void writeValueOfNodeByType(JsonWriter writer, SimpleNode node, TypeDefinition type) + throws IOException { if (!(node.getValue() instanceof String)) { throw new IllegalStateException("Value in SimpleNode should be type String"); } - + String value = (String) node.getValue(); - // TODO check Leafref, InstanceIdentifierTypeDefinition, IdentityrefTypeDefinition, UnionTypeDefinition - if (type.getBaseType() != null) { - writeValueOfNodeByType(writer, node, type.getBaseType()); - } else if (type instanceof InstanceIdentifierTypeDefinition) { - writer.value(((InstanceIdentifierTypeDefinition) type).getPathStatement().toString()); - } else if (type instanceof DecimalTypeDefinition - || type instanceof IntegerTypeDefinition - || type instanceof UnsignedIntegerTypeDefinition) { + // TODO check Leafref, InstanceIdentifierTypeDefinition, + // IdentityrefTypeDefinition, UnionTypeDefinition + TypeDefinition baseType = resolveBaseTypeFrom(type); + if (baseType instanceof InstanceIdentifierTypeDefinition) { + writer.value(((InstanceIdentifierTypeDefinition) baseType).getPathStatement().toString()); + } else if (baseType instanceof UnionTypeDefinition) { + processTypeIsUnionType(writer, (UnionTypeDefinition) baseType, value); + } else if (baseType instanceof DecimalTypeDefinition || baseType instanceof IntegerTypeDefinition + || baseType instanceof UnsignedIntegerTypeDefinition) { writer.value(new NumberForJsonWriter(value)); - } else if (type instanceof BooleanTypeDefinition) { + } else if (baseType instanceof BooleanTypeDefinition) { writer.value(Boolean.parseBoolean(value)); - } else if (type instanceof EmptyTypeDefinition) { - writer.beginArray(); - writer.nullValue(); - writer.endArray(); + } else if (baseType instanceof EmptyTypeDefinition) { + writeEmptyDataTypeToJson(writer); } else { writer.value(value != null ? value : ""); } } - + + private void processTypeIsUnionType(JsonWriter writer, UnionTypeDefinition unionType, String value) + throws IOException { + if (value == null) { + writeEmptyDataTypeToJson(writer); + } else if ((isNumber(value)) + && containsType(unionType, UnsignedIntegerTypeDefinition.class, IntegerTypeDefinition.class, + DecimalTypeDefinition.class)) { + writer.value(new NumberForJsonWriter(value)); + } else if (isBoolean(value) && containsType(unionType, BooleanTypeDefinition.class)) { + writer.value(Boolean.parseBoolean(value)); + } else { + writer.value(value); + } + } + + private boolean isBoolean(String value) { + if (value.equals("true") || value.equals("false")) { + return true; + } + return false; + } + + private void writeEmptyDataTypeToJson(JsonWriter writer) throws IOException { + writer.beginArray(); + writer.nullValue(); + writer.endArray(); + } + + private boolean isNumber(String value) { + try { + Double.valueOf(value); + } catch (NumberFormatException e) { + return false; + } + return true; + } + + private boolean containsType(UnionTypeDefinition unionType, Class... searchedTypes) { + List> allUnionSubtypes = resolveAllUnionSubtypesFrom(unionType); + + for (TypeDefinition unionSubtype : allUnionSubtypes) { + for (Class searchedType : searchedTypes) { + if (searchedType.isInstance(unionSubtype)) { + return true; + } + } + } + return false; + } + + private List> resolveAllUnionSubtypesFrom(UnionTypeDefinition inputType) { + List> result = new ArrayList<>(); + for (TypeDefinition subtype : inputType.getTypes()) { + TypeDefinition resolvedSubtype = subtype; + + resolvedSubtype = resolveBaseTypeFrom(subtype); + + if (resolvedSubtype instanceof UnionTypeDefinition) { + List> subtypesFromRecursion = resolveAllUnionSubtypesFrom((UnionTypeDefinition) resolvedSubtype); + result.addAll(subtypesFromRecursion); + } else { + result.add(resolvedSubtype); + } + } + + return result; + } + + private TypeDefinition resolveBaseTypeFrom(TypeDefinition type) { + return type.getBaseType() != null ? resolveBaseTypeFrom(type.getBaseType()) : type; + } + private static final class NumberForJsonWriter extends Number { - + private static final long serialVersionUID = -3147729419814417666L; private final String value; - + public NumberForJsonWriter(String value) { this.value = value; } @@ -219,7 +277,7 @@ class JsonMapper { public String toString() { return value; } - + } } diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/FromJsonToCompositeNodeTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/FromJsonToCompositeNodeTest.java index ede225c709..2e8b071519 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/FromJsonToCompositeNodeTest.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/FromJsonToCompositeNodeTest.java @@ -90,7 +90,7 @@ public class FromJsonToCompositeNodeTest { } @Test - public void nullArrayToCompositeNodeWithNullValueTest() { + public void nullArrayToSimpleNodeWithNullValueTest() { CompositeNode compositeNode = compositeContainerFromJson("/json-to-composite-node/array-with-null.json", true); assertNotNull(compositeNode); assertEquals("cont", compositeNode.getNodeType().getLocalName()); diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/TestUtils.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/TestUtils.java index 1d8d7495f9..c43de5d9e8 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/TestUtils.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/TestUtils.java @@ -320,15 +320,15 @@ final class TestUtils { if (modules.size() < 1) { return null; } - - Module moduleRes = null; + + Module moduleRes = null; if (modules.size() > 1) { if (moduleName == null) { return null; } else { - for (Module module: modules) { + for (Module module : modules) { if (module.getName().equals(moduleName)) { - moduleRes = module; + moduleRes = module; } } if (moduleRes == null) { @@ -338,7 +338,7 @@ final class TestUtils { } else { moduleRes = modules.iterator().next(); } - + if (moduleRes.getChildNodes() == null) { return null; } diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/ToJsonBasicDataTypesTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/ToJsonBasicDataTypesTest.java index f88a335f18..69de9f86c1 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/ToJsonBasicDataTypesTest.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/ToJsonBasicDataTypesTest.java @@ -18,10 +18,6 @@ public class ToJsonBasicDataTypesTest { @Test public void simpleYangDataTest() { String jsonOutput; - // jsonOutput = - // TestUtils.readJsonFromFile("/yang-to-json-conversion/simple-yang-types/xml/awaited_output.json", - // false); - jsonOutput = TestUtils.convertCompositeNodeDataAndYangToJson( TestUtils.loadCompositeNode("/yang-to-json-conversion/simple-data-types/xml/data.xml"), "/yang-to-json-conversion/simple-data-types", "/yang-to-json-conversion/simple-data-types/xml"); @@ -73,7 +69,7 @@ public class ToJsonBasicDataTypesTest { // boolean lfref1Checked = false; boolean lfemptyChecked = false; boolean lfstr1Checked = false; - + while (jReader.hasNext()) { String keyName = jReader.nextName(); JsonToken peek = null; @@ -148,10 +144,8 @@ public class ToJsonBasicDataTypesTest { jReader.nextNull(); jReader.endArray(); lfemptyChecked = true; - // TODO: test will be implemented when functionality will be - // implemented - } else if (keyName.equals("lflstunion")) { - jReader.skipValue(); + } else if (keyName.startsWith("lfunion")) { + checkLfUnion(jReader, keyName, peek); } else { assertTrue("Key " + keyName + " doesn't exists in yang file.", false); } @@ -180,4 +174,44 @@ public class ToJsonBasicDataTypesTest { jReader.endObject(); } + + private void checkLfUnion(JsonReader jReader, String keyName, JsonToken peek) throws IOException { + if (keyName.equals("lfunion1")) { + assertEquals("Key " + keyName + " has incorrect type", JsonToken.NUMBER, peek); + jReader.nextString(); + } else if (keyName.equals("lfunion2")) { + assertEquals("Key " + keyName + " has incorrect type", JsonToken.NUMBER, peek); + jReader.nextString(); + } else if (keyName.equals("lfunion3")) { + assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek); + jReader.nextInt(); + } else if (keyName.equals("lfunion4")) { + assertEquals("Key " + keyName + " has incorrect type", JsonToken.BOOLEAN, peek); + jReader.nextBoolean(); + } else if (keyName.equals("lfunion5")) { + assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek); + jReader.nextString(); + } else if (keyName.equals("lfunion6")) { + assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek); + jReader.nextString(); + } else if (keyName.equals("lfunion7")) { + assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek); + jReader.nextString(); + } else if (keyName.equals("lfunion8")) { + assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek); + jReader.nextString(); + } else if (keyName.equals("lfunion9")) { + assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek); + jReader.nextString(); + } else if (keyName.equals("lfunion10")) { + assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek); + jReader.nextString(); + } else if (keyName.equals("lfunion11")) { + assertEquals("Key " + keyName + " has incorrect type", JsonToken.NUMBER, peek); + jReader.nextString(); + } else if (keyName.equals("lfunion12")) { + assertEquals("Key " + keyName + " has incorrect type", JsonToken.BOOLEAN, peek); + jReader.nextBoolean(); + } + } } diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/yang-to-json-conversion/simple-data-types/simple-data-types.yang b/opendaylight/md-sal/sal-rest-connector/src/test/resources/yang-to-json-conversion/simple-data-types/simple-data-types.yang index 010d3b1c2b..759b3ecf71 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/resources/yang-to-json-conversion/simple-data-types/simple-data-types.yang +++ b/opendaylight/md-sal/sal-rest-connector/src/test/resources/yang-to-json-conversion/simple-data-types/simple-data-types.yang @@ -5,6 +5,45 @@ module simple-data-types { revision 2013-11-12 { } + typedef tpdfempty { + type empty; + } + + typedef tpdfbit { + type bits { + bit b1; + bit b2; + bit b3; + } + } + + typedef tpdfun4 { + type boolean; + } + + typedef tpdfun3 { + type union { + type tpdfbit; + type tpdfempty; + } + } + + typedef tpdfun2 { + type union { + type tpdfun3; + type tpdfun4; + } + } + + typedef tpdfun1 { + type union { + type uint8; + type decimal64 { + fraction-digits 2; + } + } + } + container cont { leaf lfnint8Min { type int8; @@ -125,12 +164,87 @@ module simple-data-types { type empty; } - leaf-list lflstunion { + leaf lfunion1 { type union { type uint16; type string; } } + leaf lfunion2 { + type union { + type decimal64 { + fraction-digits 2; + } + type string; + } + } + + leaf lfunion3 { + type union { + type empty; + type string; + } + } + + leaf lfunion4 { + type union { + type boolean; + type string; + } + } + + leaf lfunion5 { + type union { + type uint16; + type string; + } + } + + leaf lfunion6 { + type union { + type uint16; + type empty; + } + } + + leaf lfunion7 { + type tpdfun3; + } + + leaf lfunion8 { + type union { + type uint16; + type string; + } + } + + leaf lfunion9 { + type union { + type uint16; + type boolean; + } + } + + leaf lfunion10 { + type union { + type bits { + bit bt1; + bit bt2; + } + type boolean; + } + } + + leaf lfunion11 { + type union { + type tpdfun1; + type tpdfun2; + } + } + + leaf lfunion12 { + type tpdfun2; + } } diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/yang-to-json-conversion/simple-data-types/xml/data.xml b/opendaylight/md-sal/sal-rest-connector/src/test/resources/yang-to-json-conversion/simple-data-types/xml/data.xml index df00ca917e..0d31e9037a 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/resources/yang-to-json-conversion/simple-data-types/xml/data.xml +++ b/opendaylight/md-sal/sal-rest-connector/src/test/resources/yang-to-json-conversion/simple-data-types/xml/data.xml @@ -24,8 +24,16 @@ bit3 AAaacdabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ%%-#^ - 324 - 33.3 - lfunion - true + 324 + 33.3 + 55 + true + true + false + + + + bt1 + 33 + false \ No newline at end of file