Merge "Bug 1817 - Add in ietf-restonf to the yangtools-models feature."
authorTony Tkacik <ttkacik@cisco.com>
Fri, 12 Sep 2014 13:58:03 +0000 (13:58 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Fri, 12 Sep 2014 13:58:03 +0000 (13:58 +0000)
68 files changed:
code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/LazyGeneratedCodecRegistry.java
code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/TransformerGenerator.xtend
code-generator/binding-generator-impl/src/test/java/org/opendaylight/yangtools/sal/binding/generator/impl/AugmentRelativeXPathTest.java
code-generator/binding-generator-impl/src/test/java/org/opendaylight/yangtools/sal/binding/generator/impl/AugmentedTypeTest.java
code-generator/binding-generator-impl/src/test/java/org/opendaylight/yangtools/sal/binding/generator/impl/BinaryTypeTest.java
code-generator/binding-generator-impl/src/test/java/org/opendaylight/yangtools/sal/binding/generator/impl/BitAndUnionTOEnclosingTest.java
code-generator/binding-generator-impl/src/test/java/org/opendaylight/yangtools/sal/binding/generator/impl/ChoiceCaseGenTypesTest.java
code-generator/binding-generator-impl/src/test/java/org/opendaylight/yangtools/sal/binding/generator/impl/ControllerTest.java
code-generator/binding-generator-impl/src/test/java/org/opendaylight/yangtools/sal/binding/generator/impl/ExtendedTypedefTest.java
code-generator/binding-generator-impl/src/test/java/org/opendaylight/yangtools/sal/binding/generator/impl/GenEnumResolvingTest.java
code-generator/binding-generator-impl/src/test/java/org/opendaylight/yangtools/sal/binding/generator/impl/GenTypesSubSetTest.java
code-generator/binding-generator-impl/src/test/java/org/opendaylight/yangtools/sal/binding/generator/impl/GenerateInnerClassForBitsAndUnionInLeavesTest.java
code-generator/binding-generator-impl/src/test/java/org/opendaylight/yangtools/sal/binding/generator/impl/GeneratedTypesBitsTest.java
code-generator/binding-generator-impl/src/test/java/org/opendaylight/yangtools/sal/binding/generator/impl/GeneratedTypesLeafrefTest.java
code-generator/binding-generator-impl/src/test/java/org/opendaylight/yangtools/sal/binding/generator/impl/GeneratedTypesStringTest.java
code-generator/binding-generator-impl/src/test/java/org/opendaylight/yangtools/sal/binding/generator/impl/GeneratedTypesTest.java
code-generator/binding-generator-impl/src/test/java/org/opendaylight/yangtools/sal/binding/generator/impl/IdentityrefTypeTest.java
code-generator/binding-generator-impl/src/test/java/org/opendaylight/yangtools/sal/binding/generator/impl/UnionTypeDefTest.java
code-generator/binding-generator-impl/src/test/java/org/opendaylight/yangtools/sal/binding/generator/impl/UsesTest.java
common/util/src/main/java/org/opendaylight/yangtools/util/DurationStatsTracker.java
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/AsyncNotifyingListenableFutureTask.java
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/CountingRejectedExecutionHandler.java
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/DeadlockDetectingListeningExecutorService.java
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/SettableBoolean.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/SettableBooleanThreadLocal.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/TrackingLinkedBlockingQueue.java
restconf/restconf-util/src/main/java/org/opendaylight/yangtools/restconf/utils/RestconfUtils.java
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/QName.java
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/AbstractJSONCodec.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/CompositeNodeDataWithSchema.java
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONCodec.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONCodecFactory.java [moved from yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/CodecFactory.java with 55% similarity]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONLeafrefCodec.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONNormalizedNodeStreamWriter.java
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterContext.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterInvisibleContext.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterListContext.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterNamedObjectContext.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterObjectContext.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterQNameContext.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterRootContext.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterURIContext.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStringIdentityrefCodec.java
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStringInstanceIdentifierCodec.java
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JsonParserStream.java
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/QuotedJSONCodec.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/UnquotedJSONCodec.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/JsonStreamToNormalizedNodeTest.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/NormalizedNodeToJsonStreamTest.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/StreamToNormalizedNodeTest.java
yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/TestUtils.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/TestingNormalizedNodeStructuresCreator.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/case-node-augmentation-in-choice-in-container.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/case-node-external-augmentation-in-choice-in-container.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/choice-node-augmentation-in-container.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/choice-node-in-container.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/keyed-list-node-in-container.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/leaf-node-in-container.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/leaf-node-via-augmentation-in-container.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/leaflist-node-in-container.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/unkeyed-node-in-container.json [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/AbstractImmutableDataContainerNode.java
yang/yang-model-api/pom.xml
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SourceIdentifier.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/PotentialSchemaSource.java
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaRepository.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangParserListenerImpl.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaContextFactory.java

index f72856e1c11b540c2c6d855ef26b0ab7455a209e..65e80d05843cb5566be494788b2551d6980dd607 100644 (file)
@@ -984,8 +984,21 @@ class LazyGeneratedCodecRegistry implements CodecRegistry, SchemaContextListener
 
         @Override
         protected void adaptForPathImpl(final InstanceIdentifier<?> augTarget, final DataNodeContainer ctxNode) {
-            Optional<ChoiceNode> newChoice = BindingSchemaContextUtils.findInstantiatedChoice(ctxNode, choiceType);
             tryToLoadImplementations();
+            Optional<ChoiceNode> newChoice = BindingSchemaContextUtils.findInstantiatedChoice(ctxNode, choiceType);
+            if(!newChoice.isPresent()) {
+                // Choice is nested inside other choice, so we need to look two levels deep.
+                in_choices: for(DataSchemaNode child : ctxNode.getChildNodes()) {
+                    if(child instanceof ChoiceNode) {
+                        Optional<ChoiceNode> potential = findChoiceInChoiceCases((ChoiceNode) child, choiceType);
+                        if(potential.isPresent()) {
+                            newChoice = potential;
+                            break in_choices;
+                        }
+                    }
+                }
+            }
+
             Preconditions.checkState(newChoice.isPresent(), "BUG: Unable to find instantiated choice node in schema.");
             for (@SuppressWarnings("rawtypes")
             Entry<Class, ChoiceCaseCodecImpl<?>> codec : getImplementations().entrySet()) {
@@ -998,6 +1011,16 @@ class LazyGeneratedCodecRegistry implements CodecRegistry, SchemaContextListener
             }
         }
 
+        private Optional<ChoiceNode> findChoiceInChoiceCases(ChoiceNode choice, Class<?> choiceType) {
+            for(ChoiceCaseNode caze : choice.getCases()) {
+                Optional<ChoiceNode> potential = BindingSchemaContextUtils.findInstantiatedChoice(caze, choiceType);
+                if(potential.isPresent()) {
+                    return potential;
+                }
+            }
+            return Optional.absent();
+        }
+
         @Override
         public String toString() {
             return "DispatchChoiceCodecImpl [choiceType=" + choiceType + "]";
index 473c8e644c0749ad757c7f06b8578c3d4fd01fdc..1a9914828a8b2891a69fbca4adafde56f97d11cf 100644 (file)
@@ -1435,7 +1435,7 @@ class TransformerGenerator extends AbstractTransformerGenerator {
     private def staticQNameField(CtClass it, QName node, SourceCodeGenerator sourceGenerator) {
         val field = new CtField(ctQName, "QNAME", it);
         field.modifiers = PUBLIC + FINAL + STATIC;
-        val code = '''«QName.asCtClass.name».create("«node.namespace»","«node.formattedRevision»","«node.localName»")'''
+        val code = '''«QName.asCtClass.name».cachedReference(«QName.asCtClass.name».create("«node.namespace»","«node.formattedRevision»","«node.localName»"))'''
         addField(field, code )
 
         sourceGenerator.appendField( field, code );
index de566eea1ec6c46eb40a5969738167be25d75186..250faf8c280c00384eb0974ef89235c9f0a25be2 100644 (file)
@@ -13,6 +13,7 @@ import static org.junit.Assert.assertTrue;
 
 import java.io.IOException;
 import java.util.List;
+
 import org.junit.Test;
 import org.opendaylight.yangtools.sal.binding.generator.api.BindingGenerator;
 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty;
@@ -36,7 +37,7 @@ public class AugmentRelativeXPathTest extends AbstractTypesTest {
         final SchemaContext context = parser.parseFiles(testModels);
 
         assertNotNull("context is null", context);
-        final BindingGenerator bindingGen = new BindingGeneratorImpl();
+        final BindingGenerator bindingGen = new BindingGeneratorImpl(true);
         final List<Type> genTypes = bindingGen.generateTypes(context);
 
         assertNotNull("genTypes is null", genTypes);
index ba5d1c984c9f730b371871cb52ebd9118bafb6d8..70c7c6684e601be7bd8263d21dbd304a6efb03be 100644 (file)
@@ -14,6 +14,7 @@ import static org.junit.Assert.assertTrue;
 import java.io.File;
 import java.util.Arrays;
 import java.util.List;
+
 import org.junit.Test;
 import org.opendaylight.yangtools.sal.binding.generator.api.BindingGenerator;
 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty;
@@ -43,7 +44,7 @@ public class AugmentedTypeTest {
                 augmentNetworkLink, augmentTopologyTunnels, ietfInterfaces));
         assertNotNull("Schema Context is null", context);
 
-        final BindingGenerator bindingGen = new BindingGeneratorImpl();
+        final BindingGenerator bindingGen = new BindingGeneratorImpl(true);
         final List<Type> genTypes = bindingGen.generateTypes(context);
 
         assertNotNull("genTypes is null", genTypes);
index f5c2c1b2aa65b902ac0aabbd606d4c35b6a3d100..32b51af1931243be4d9b164a41203734c2deaed4 100644 (file)
@@ -16,6 +16,7 @@ import java.net.URISyntaxException;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.List;
+
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.opendaylight.yangtools.sal.binding.generator.api.BindingGenerator;
@@ -45,7 +46,7 @@ public class BinaryTypeTest {
         final SchemaContext context = parser.parseFiles(yangModels);
 
         assertNotNull("context is null", context);
-        final BindingGenerator bindingGen = new BindingGeneratorImpl();
+        final BindingGenerator bindingGen = new BindingGeneratorImpl(true);
         final List<Type> genTypes = bindingGen.generateTypes(context);
 
         assertNotNull("genTypes is null", genTypes);
index 030cd11757544a5ceb2eb131680560f22c309e4a..419551a2d7bdcf5053cdd6649fb062d11e7dec0f 100644 (file)
@@ -17,6 +17,7 @@ import java.io.IOException;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.List;
+
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.opendaylight.yangtools.sal.binding.generator.api.BindingGenerator;
@@ -39,7 +40,7 @@ public class BitAndUnionTOEnclosingTest {
         final SchemaContext context = parser.parseFiles(testModels);
 
         assertNotNull(context);
-        final BindingGenerator bindingGen = new BindingGeneratorImpl();
+        final BindingGenerator bindingGen = new BindingGeneratorImpl(true);
         genTypes = bindingGen.generateTypes(context);
 
         for (Type type : genTypes) {
index 7498a10a64a04eb1ed47b314c24bf1307abd571f..e7c28d7e0202a8f35bfcfb3b89437a602165641d 100644 (file)
@@ -15,6 +15,7 @@ import static org.opendaylight.yangtools.sal.binding.generator.impl.SupportTestU
 
 import java.io.IOException;
 import java.util.List;
+
 import org.junit.Test;
 import org.opendaylight.yangtools.sal.binding.generator.api.BindingGenerator;
 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedTransferObject;
@@ -30,7 +31,7 @@ public class ChoiceCaseGenTypesTest extends AbstractTypesTest {
         super(ChoiceCaseGenTypesTest.class.getResource("/choice-case-type-test-models"));
     }
 
-    private GeneratedType checkGeneratedType(List<Type> genTypes, String genTypeName, String packageName, int occurences) {
+    private GeneratedType checkGeneratedType(final List<Type> genTypes, final String genTypeName, final String packageName, final int occurences) {
         GeneratedType searchedGenType = null;
         int searchedGenTypeCounter = 0;
         for (Type type : genTypes) {
@@ -49,7 +50,7 @@ public class ChoiceCaseGenTypesTest extends AbstractTypesTest {
 
     }
 
-    private GeneratedType checkGeneratedType(List<Type> genTypes, String genTypeName, String packageName) {
+    private GeneratedType checkGeneratedType(final List<Type> genTypes, final String genTypeName, final String packageName) {
         return checkGeneratedType(genTypes, genTypeName, packageName, 1);
     }
 
@@ -59,7 +60,7 @@ public class ChoiceCaseGenTypesTest extends AbstractTypesTest {
         final SchemaContext context = parser.parseFiles(testModels);
 
         assertNotNull("context is null", context);
-        final BindingGenerator bindingGen = new BindingGeneratorImpl();
+        final BindingGenerator bindingGen = new BindingGeneratorImpl(true);
         final List<Type> genTypes = bindingGen.generateTypes(context);
 
         assertNotNull("genTypes is null", genTypes);
index 221ff7807643fb7d0ea608565745ccc6e49f50b0..0fde6324ef904a0763e612d15522360274af0359 100644 (file)
@@ -13,6 +13,7 @@ import static org.junit.Assert.assertTrue;
 import java.io.File;
 import java.util.Arrays;
 import java.util.List;
+
 import org.junit.Test;
 import org.opendaylight.yangtools.sal.binding.generator.api.BindingGenerator;
 import org.opendaylight.yangtools.sal.binding.model.api.Type;
@@ -30,7 +31,7 @@ public class ControllerTest {
         final SchemaContext context = new YangParserImpl().parseFiles(Arrays.asList(cn, co, ietfInetTypes));
         assertNotNull("Schema Context is null", context);
 
-        final BindingGenerator bindingGen = new BindingGeneratorImpl();
+        final BindingGenerator bindingGen = new BindingGeneratorImpl(true);
         final List<Type> genTypes = bindingGen.generateTypes(context);
 
         assertNotNull(genTypes);
index 27ac8add50543a79f35b678b6ee2588802a22f2b..329934fb3091db0e97f7f44632225098230df304 100644 (file)
@@ -15,6 +15,7 @@ import static org.junit.Assert.assertTrue;
 import java.io.File;
 import java.util.Arrays;
 import java.util.List;
+
 import org.junit.Test;
 import org.opendaylight.yangtools.sal.binding.generator.api.BindingGenerator;
 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty;
@@ -34,7 +35,7 @@ public class ExtendedTypedefTest {
         final SchemaContext context = new YangParserImpl().parseFiles(Arrays.asList(abstractTopology, ietfInetTypes));
         assertNotNull("Schema Context is null", context);
 
-        final BindingGenerator bindingGen = new BindingGeneratorImpl();
+        final BindingGenerator bindingGen = new BindingGeneratorImpl(true);
         final List<Type> genTypes = bindingGen.generateTypes(context);
 
         GeneratedTransferObject simpleTypedef4 = null;
index c00afcf2e8581b332ab80d5fcd21ff5cddb4ab51..1227d70e3ab7f6806d39b7d3b1a86d53258ac031 100644 (file)
@@ -17,6 +17,7 @@ import java.net.URISyntaxException;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+
 import org.junit.Test;
 import org.opendaylight.yangtools.sal.binding.generator.api.BindingGenerator;
 import org.opendaylight.yangtools.sal.binding.model.api.Enumeration;
@@ -37,7 +38,7 @@ public class GenEnumResolvingTest {
         final SchemaContext context = new YangParserImpl().parseFiles(Arrays.asList(ietfInterfaces, ianaIfTypeModel));
         assertTrue(context != null);
 
-        final BindingGenerator bindingGen = new BindingGeneratorImpl();
+        final BindingGenerator bindingGen = new BindingGeneratorImpl(true);
         final List<Type> genTypes = bindingGen.generateTypes(context);
         assertTrue(genTypes != null);
 
@@ -98,7 +99,7 @@ public class GenEnumResolvingTest {
         File ianaIfType = new File(getClass().getResource("/ietf/iana-if-type.yang").toURI());
         final SchemaContext context = new YangParserImpl().parseFiles(Collections.singleton(ianaIfType));
         assertTrue(context != null);
-        final BindingGenerator bindingGen = new BindingGeneratorImpl();
+        final BindingGenerator bindingGen = new BindingGeneratorImpl(true);
         final List<Type> genTypes = bindingGen.generateTypes(context);
         assertTrue(genTypes != null);
         assertEquals(1, genTypes.size());
@@ -121,7 +122,7 @@ public class GenEnumResolvingTest {
         final SchemaContext context = new YangParserImpl().parseFiles(Arrays.asList(abstractTopology, ietfInterfaces,
                 ianaIfType));
         assertNotNull(context);
-        final BindingGenerator bindingGen = new BindingGeneratorImpl();
+        final BindingGenerator bindingGen = new BindingGeneratorImpl(true);
         final List<Type> genTypes = bindingGen.generateTypes(context);
         assertNotNull(genTypes);
         assertTrue(!genTypes.isEmpty());
index 59f0e135f4538356f61f052859b04de8308e1700..5f3988a0f6584eff8ff25e37348ea9aea980a030 100644 (file)
@@ -16,6 +16,7 @@ import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+
 import org.junit.Test;
 import org.opendaylight.yangtools.sal.binding.generator.api.BindingGenerator;
 import org.opendaylight.yangtools.sal.binding.model.api.Type;
@@ -48,7 +49,7 @@ public class GenTypesSubSetTest {
 
         assertEquals("Set of to Generate Modules must contain 2 modules", 2, toGenModules.size());
         assertNotNull("Schema Context is null", context);
-        final BindingGenerator bindingGen = new BindingGeneratorImpl();
+        final BindingGenerator bindingGen = new BindingGeneratorImpl(true);
         final List<Type> genTypes = bindingGen.generateTypes(context, toGenModules);
         assertNotNull("genTypes is null", genTypes);
         assertFalse("genTypes is empty", genTypes.isEmpty());
@@ -82,7 +83,7 @@ public class GenTypesSubSetTest {
         }
         assertEquals("Set of to Generate Modules must contain 3 modules", 3, toGenModules.size());
 
-        final BindingGenerator bindingGen = new BindingGeneratorImpl();
+        final BindingGenerator bindingGen = new BindingGeneratorImpl(true);
         final List<Type> genTypes = bindingGen.generateTypes(context, toGenModules);
         assertNotNull("genTypes is null", genTypes);
         assertFalse("genTypes is empty", genTypes.isEmpty());
index cc03ea4aafee6c38d503a5ca89668f2f16323604..d48f736fda39319e4ac1ca34c6f6df02c0251081 100644 (file)
@@ -13,9 +13,9 @@ import java.io.File;
 import java.net.URI;
 import java.util.ArrayList;
 import java.util.List;
+
 import org.junit.Test;
 import org.opendaylight.yangtools.sal.binding.generator.api.BindingGenerator;
-import org.opendaylight.yangtools.sal.binding.generator.impl.BindingGeneratorImpl;
 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty;
 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedTransferObject;
 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType;
@@ -30,8 +30,8 @@ public class GenerateInnerClassForBitsAndUnionInLeavesTest {
         final YangContextParser parser = new YangParserImpl();
 
         final List<File> inputFiles = new ArrayList<File>();
-        for (int i = 0; i < yangFiles.length; ++i) {
-            inputFiles.add(new File(yangFiles[i]));
+        for (URI yangFile : yangFiles) {
+            inputFiles.add(new File(yangFile));
         }
 
         return parser.parseFiles(inputFiles);
@@ -44,7 +44,7 @@ public class GenerateInnerClassForBitsAndUnionInLeavesTest {
         final SchemaContext context = resolveSchemaContextFromFiles(yangTypesPath);
         assertTrue(context != null);
 
-        final BindingGenerator bindingGen = new BindingGeneratorImpl();
+        final BindingGenerator bindingGen = new BindingGeneratorImpl(true);
         final List<Type> genTypes = bindingGen.generateTypes(context);
         assertTrue(genTypes != null);
 
index 1dc8f00dde6757309805380aa0ff635f7c558fb2..16abfd7b1c9fcc47f434ce84bc4bb6158245a999 100644 (file)
@@ -14,6 +14,7 @@ import static org.junit.Assert.assertTrue;
 
 import java.net.URI;
 import java.util.List;
+
 import org.junit.Test;
 import org.opendaylight.yangtools.sal.binding.generator.api.BindingGenerator;
 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty;
@@ -34,7 +35,7 @@ public class GeneratedTypesBitsTest {
         final SchemaContext context = SupportTestUtil.resolveSchemaContextFromFiles(yangTypesPath);
         assertTrue(context != null);
 
-        final BindingGenerator bindingGen = new BindingGeneratorImpl();
+        final BindingGenerator bindingGen = new BindingGeneratorImpl(true);
         final List<Type> genTypes = bindingGen.generateTypes(context);
         assertTrue(genTypes != null);
 
index 1d8649be272f4f209deb5e59e2e9613d534c02bf..70d242ee3f0fb73f94190abefcac8db58171234b 100644 (file)
@@ -13,6 +13,7 @@ import static org.junit.Assert.assertNotSame;
 import static org.junit.Assert.fail;
 
 import com.google.common.io.ByteSource;
+
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -23,6 +24,7 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 import java.util.Set;
+
 import org.junit.Test;
 import org.opendaylight.yangtools.sal.binding.generator.api.BindingGenerator;
 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty;
@@ -51,8 +53,8 @@ public class GeneratedTypesLeafrefTest {
         final YangContextParser parser = new YangParserImpl();
 
         final List<File> inputFiles = new ArrayList<File>();
-        for (int i = 0; i < yangFiles.length; ++i) {
-            inputFiles.add(new File(yangFiles[i]));
+        for (URI yangFile : yangFiles) {
+            inputFiles.add(new File(yangFile));
         }
 
         return parser.parseFiles(inputFiles);
@@ -71,7 +73,7 @@ public class GeneratedTypesLeafrefTest {
         assertNotNull(context);
         assertEquals(4, context.getModules().size());
 
-        final BindingGenerator bindingGen = new BindingGeneratorImpl();
+        final BindingGenerator bindingGen = new BindingGeneratorImpl(true);
         final List<Type> genTypes = bindingGen.generateTypes(context);
 
         assertEquals(54, genTypes.size());
@@ -252,7 +254,7 @@ public class GeneratedTypesLeafrefTest {
         assertNotNull(context);
         assertEquals(1, context.getModules().size());
 
-        final BindingGenerator bindingGen = new BindingGeneratorImpl();
+        final BindingGenerator bindingGen = new BindingGeneratorImpl(true);
         try {
             bindingGen.generateTypes(context);
             fail("Expected IllegalArgumentException caused by invalid leafref path");
index d0f15174f384d2aa3e58d8d4d05dfc4d92b56ccd..fa7e0bfc59a9d679fd35ae50405d70083470f776 100644 (file)
@@ -15,6 +15,7 @@ import java.io.IOException;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.List;
+
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.opendaylight.yangtools.sal.binding.generator.api.BindingGenerator;
@@ -43,7 +44,7 @@ public class GeneratedTypesStringTest {
         final SchemaContext context = parser.parseFiles(testModels);
 
         assertNotNull(context);
-        final BindingGenerator bindingGen = new BindingGeneratorImpl();
+        final BindingGenerator bindingGen = new BindingGeneratorImpl(true);
         final List<Type> genTypes = bindingGen.generateTypes(context);
 
         boolean typedefStringFound = false;
@@ -64,35 +65,41 @@ public class GeneratedTypesStringTest {
                     for (Constant con : constants) {
                         if (con.getName().equals("PATTERN_CONSTANTS")) {
                             constantRegExListFound = true;
-                        } else
+                        } else {
                             break;
+                        }
                         ParameterizedType pType;
                         if (con.getType() instanceof ParameterizedType) {
                             pType = (ParameterizedType) con.getType();
-                        } else
+                        } else {
                             break;
+                        }
 
                         Type[] types;
                         if (pType.getName().equals("List")) {
                             constantRegExListTypeContainer = true;
                             types = pType.getActualTypeArguments();
-                        } else
+                        } else {
                             break;
+                        }
 
                         if (types.length == 1) {
                             constantRegExListTypeOneGeneric = true;
-                        } else
+                        } else {
                             break;
+                        }
 
                         if (types[0].getName().equals("String")) {
                             constantRegExListTypeGeneric = true;
-                        } else
+                        } else {
                             break;
+                        }
 
                         if (con.getValue() instanceof List) {
                             constantRegExListValueOK = true;
-                        } else
+                        } else {
                             break;
+                        }
 
                         for (Object obj : (List<?>) con.getValue()) {
                             if (!(obj instanceof String)) {
index f929414609a10a2f2670abcc6056f2528b238595..6ab45367de7d339128e4325c339909cb9ac7576f 100644 (file)
@@ -16,6 +16,7 @@ import java.net.URISyntaxException;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+
 import org.junit.Test;
 import org.opendaylight.yangtools.sal.binding.generator.api.BindingGenerator;
 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty;
@@ -36,7 +37,7 @@ public class GeneratedTypesTest {
         final SchemaContext context = new YangParserImpl().parseFiles(Arrays.asList(abstractTopology, ietfInetTypes));
         assertNotNull(context);
 
-        final BindingGenerator bindingGen = new BindingGeneratorImpl();
+        final BindingGenerator bindingGen = new BindingGeneratorImpl(true);
         final List<Type> genTypes = bindingGen.generateTypes(context);
 
         assertNotNull(genTypes);
@@ -49,7 +50,7 @@ public class GeneratedTypesTest {
         final SchemaContext context = new YangParserImpl().parseFiles(Collections.singleton(testFile));
         assertNotNull(context);
 
-        final BindingGenerator bindingGen = new BindingGeneratorImpl();
+        final BindingGenerator bindingGen = new BindingGeneratorImpl(true);
         final List<Type> genTypes = bindingGen.generateTypes(context);
 
         assertNotNull(genTypes);
@@ -134,7 +135,7 @@ public class GeneratedTypesTest {
         final SchemaContext context = new YangParserImpl().parseFiles(Collections.singleton(testFile));
         assertNotNull(context);
 
-        final BindingGenerator bindingGen = new BindingGeneratorImpl();
+        final BindingGenerator bindingGen = new BindingGeneratorImpl(true);
         final List<Type> genTypes = bindingGen.generateTypes(context);
 
         assertNotNull(genTypes);
@@ -218,7 +219,7 @@ public class GeneratedTypesTest {
         final SchemaContext context = new YangParserImpl().parseFiles(Collections.singleton(testFile));
         assertNotNull(context);
 
-        final BindingGenerator bindingGen = new BindingGeneratorImpl();
+        final BindingGenerator bindingGen = new BindingGeneratorImpl(true);
         final List<Type> genTypes = bindingGen.generateTypes(context);
 
         assertNotNull(genTypes);
@@ -341,7 +342,7 @@ public class GeneratedTypesTest {
         final SchemaContext context = new YangParserImpl().parseFiles(Collections.singleton(testFile));
         assertNotNull(context);
 
-        final BindingGenerator bindingGen = new BindingGeneratorImpl();
+        final BindingGenerator bindingGen = new BindingGeneratorImpl(true);
         final List<Type> genTypes = bindingGen.generateTypes(context);
 
         assertNotNull(genTypes);
@@ -393,7 +394,7 @@ public class GeneratedTypesTest {
         final SchemaContext context = new YangParserImpl().parseFiles(Collections.singleton(testFile));
         assertNotNull(context);
 
-        final BindingGenerator bindingGen = new BindingGeneratorImpl();
+        final BindingGenerator bindingGen = new BindingGeneratorImpl(true);
         final List<Type> genTypes = bindingGen.generateTypes(context);
 
         assertNotNull(genTypes);
index ff81f55bebbe62a670986625e080471e7b6905f4..0c20b91b01e6cdad970f5fd9744279a9933e1075 100644 (file)
@@ -16,6 +16,7 @@ import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.List;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.opendaylight.yangtools.sal.binding.generator.api.BindingGenerator;
@@ -60,7 +61,7 @@ public class IdentityrefTypeTest {
         final SchemaContext context = parser.parseFiles(testModels);
 
         assertNotNull(context);
-        final BindingGenerator bindingGen = new BindingGeneratorImpl();
+        final BindingGenerator bindingGen = new BindingGeneratorImpl(true);
         final List<Type> genTypes = bindingGen.generateTypes(context);
 
         GeneratedType moduleGenType = null;
index 76b13bfedb0f36f6c2e0a0e8e66f70e510cdeb21..ae8a3eb3d4c3d3bb180f85fb7b74d0ddacd86512 100644 (file)
@@ -13,6 +13,7 @@ import static org.junit.Assert.assertNotNull;
 import java.io.File;
 import java.util.Arrays;
 import java.util.List;
+
 import org.junit.Test;
 import org.opendaylight.yangtools.sal.binding.generator.api.BindingGenerator;
 import org.opendaylight.yangtools.sal.binding.model.api.Type;
@@ -28,7 +29,7 @@ public class UnionTypeDefTest {
         final SchemaContext context = new YangParserImpl().parseFiles(Arrays.asList(abstractTopology, ietfInetTypes));
 
         assertNotNull("context is null", context);
-        final BindingGenerator bindingGen = new BindingGeneratorImpl();
+        final BindingGenerator bindingGen = new BindingGeneratorImpl(true);
         final List<Type> genTypes = bindingGen.generateTypes(context);
 
         assertNotNull("genTypes is null", genTypes);
index 8c4cb56c361e7323b36748f776a56c216e23d063..524ef51962fd070976da7715c6d611ff82c635ae 100644 (file)
@@ -18,6 +18,7 @@ import java.io.IOException;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.List;
+
 import org.junit.Test;
 import org.opendaylight.yangtools.sal.binding.generator.api.BindingGenerator;
 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedTransferObject;
@@ -29,7 +30,7 @@ import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
 
 public class UsesTest {
 
-    private static List<File> loadTestResources(String testFile) {
+    private static List<File> loadTestResources(final String testFile) {
         try {
         final List<File> testModels = new ArrayList<File>();
         final File listModelFile = new File(UsesTest.class.getResource(testFile).toURI());
@@ -47,7 +48,7 @@ public class UsesTest {
         final SchemaContext context = parser.parseFiles(testModels);
 
         assertNotNull(context);
-        final BindingGenerator bindingGen = new BindingGeneratorImpl();
+        final BindingGenerator bindingGen = new BindingGeneratorImpl(true);
         final List<Type> genTypes = bindingGen.generateTypes(context);
         GeneratedType groupingU = null;
         GeneratedType groupingX = null;
@@ -102,7 +103,7 @@ public class UsesTest {
         final SchemaContext context = parser.parseFiles(testModels);
 
         assertNotNull(context);
-        final BindingGenerator bindingGen = new BindingGeneratorImpl();
+        final BindingGenerator bindingGen = new BindingGeneratorImpl(true);
         final List<Type> genTypes = bindingGen.generateTypes(context);
 
         GeneratedType groupingCaseTest = null;
@@ -150,7 +151,7 @@ public class UsesTest {
         final SchemaContext context = parser.parseFiles(testModels);
 
         assertNotNull(context);
-        final BindingGenerator bindingGen = new BindingGeneratorImpl();
+        final BindingGenerator bindingGen = new BindingGeneratorImpl(true);
         final List<Type> genTypes = bindingGen.generateTypes(context);
 
         int containerTestCount = 0;
@@ -203,7 +204,7 @@ public class UsesTest {
         final SchemaContext context = parser.parseFiles(testModels);
 
         assertNotNull(context);
-        final BindingGenerator bindingGen = new BindingGeneratorImpl();
+        final BindingGenerator bindingGen = new BindingGeneratorImpl(true);
         final List<Type> genTypes = bindingGen.generateTypes(context);
 
         int groupingTestCount = 0;
@@ -254,7 +255,7 @@ public class UsesTest {
         final SchemaContext context = parser.parseFiles(testModels);
 
         assertNotNull(context);
-        final BindingGenerator bindingGen = new BindingGeneratorImpl();
+        final BindingGenerator bindingGen = new BindingGeneratorImpl(true);
         final List<Type> genTypes = bindingGen.generateTypes(context);
 
         int listTestCounter = 0;
@@ -336,7 +337,7 @@ public class UsesTest {
         final SchemaContext context = parser.parseFiles(testModels);
 
         assertNotNull(context);
-        final BindingGenerator bindingGen = new BindingGeneratorImpl();
+        final BindingGenerator bindingGen = new BindingGeneratorImpl(true);
         final List<Type> genTypes = bindingGen.generateTypes(context);
 
         int groupingModulTestCounter = 0;
@@ -387,7 +388,7 @@ public class UsesTest {
         final SchemaContext context = parser.parseFiles(testModels);
 
         assertNotNull(context);
-        final BindingGenerator bindingGen = new BindingGeneratorImpl();
+        final BindingGenerator bindingGen = new BindingGeneratorImpl(true);
         final List<Type> genTypes = bindingGen.generateTypes(context);
 
         int rpcTestInputCounter = 0;
@@ -480,7 +481,7 @@ public class UsesTest {
         final SchemaContext context = parser.parseFiles(testModels);
 
         assertNotNull(context);
-        final BindingGenerator bindingGen = new BindingGeneratorImpl();
+        final BindingGenerator bindingGen = new BindingGeneratorImpl(true);
         final List<Type> genTypes = bindingGen.generateTypes(context);
 
         GeneratedType containerAugment1 = null;
@@ -533,7 +534,7 @@ public class UsesTest {
         final SchemaContext context = parser.parseFiles(testModels);
 
         assertNotNull(context);
-        final BindingGenerator bindingGen = new BindingGeneratorImpl();
+        final BindingGenerator bindingGen = new BindingGeneratorImpl(true);
         final List<Type> genTypes = bindingGen.generateTypes(context);
 
         GeneratedType notificationTest = null;
index 21690c2864e705817a6245dc2c9ec98ac1b21675..9a29dca6a8002eaf1b62f96a44baaa8c7a2d69ce 100644 (file)
@@ -13,12 +13,13 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS;
 import static java.util.concurrent.TimeUnit.NANOSECONDS;
 import static java.util.concurrent.TimeUnit.SECONDS;
 
+import com.google.common.util.concurrent.AtomicDouble;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
 import java.util.Date;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicLong;
 
-import com.google.common.util.concurrent.AtomicDouble;
-
 /**
  * Class that calculates and tracks time duration statistics.
  *
@@ -26,6 +27,8 @@ import com.google.common.util.concurrent.AtomicDouble;
  */
 public class DurationStatsTracker {
 
+    private static final DecimalFormat decimalFormat;
+
     private final AtomicLong totalDurations = new AtomicLong();
     private final AtomicLong longestDuration = new AtomicLong();
     private volatile long timeOfLongestDuration;
@@ -33,10 +36,17 @@ public class DurationStatsTracker {
     private volatile long timeOfShortestDuration;
     private final AtomicDouble averageDuration = new AtomicDouble();
 
+    static {
+        final DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance();
+        symbols.setDecimalSeparator('.');
+        decimalFormat = new DecimalFormat("0.00", symbols);
+    }
+
     /**
      * Add a duration to track.
      *
-     * @param duration the duration in nanoseconds.
+     * @param duration
+     *            the duration in nanoseconds.
      */
     public void addDuration(long duration) {
 
@@ -46,21 +56,21 @@ public class DurationStatsTracker {
         long newTotal = currentTotal + 1;
 
         // Calculate moving cumulative average.
-        double newAve = currentAve * (double)currentTotal / (double)newTotal + (double)duration / (double)newTotal;
+        double newAve = currentAve * currentTotal / newTotal + (double) duration / (double) newTotal;
 
         averageDuration.compareAndSet(currentAve, newAve);
         totalDurations.compareAndSet(currentTotal, newTotal);
 
         long longest = longestDuration.get();
-        if( duration > longest ) {
-            if(longestDuration.compareAndSet( longest, duration )) {
+        if (duration > longest) {
+            if (longestDuration.compareAndSet(longest, duration)) {
                 timeOfLongestDuration = System.currentTimeMillis();
             }
         }
 
         long shortest = shortestDuration.get();
-        if( duration < shortest ) {
-            if(shortestDuration.compareAndSet( shortest, duration )) {
+        if (duration < shortest) {
+            if (shortestDuration.compareAndSet(shortest, duration)) {
                 timeOfShortestDuration = System.currentTimeMillis();
             }
         }
@@ -122,46 +132,55 @@ public class DurationStatsTracker {
     }
 
     /**
-     * Returns the average duration as a displayable String with units, e.g. "12.34 ms".
+     * Returns the average duration as a displayable String with units, e.g.
+     * "12.34 ms".
      */
     public String getDisplayableAverageDuration() {
         return formatDuration(getAverageDuration(), 0);
     }
 
     /**
-     * Returns the shortest duration as a displayable String with units and the date/time at
-     * which it occurred, e.g. "12.34 ms at 08/02/2014 12:30:24".
+     * Returns the shortest duration as a displayable String with units and the
+     * date/time at which it occurred, e.g. "12.34 ms at 08/02/2014 12:30:24".
      */
     public String getDisplayableShortestDuration() {
         return formatDuration(getShortestDuration(), getTimeOfShortestDuration());
     }
 
     /**
-     * Returns the longest duration as a displayable String with units and the date/time at
-     * which it occurred, e.g. "12.34 ms at 08/02/2014 12:30:24".
+     * Returns the longest duration as a displayable String with units and the
+     * date/time at which it occurred, e.g. "12.34 ms at 08/02/2014 12:30:24".
      */
     public String getDisplayableLongestDuration() {
         return formatDuration(getLongestDuration(), getTimeOfLongestDuration());
     }
 
+    /**
+     * Returns formatted value of number, e.g. "12.34". Always is used dot as
+     * decimal separator.
+     */
+    private static synchronized String formatDecimalValue(double value) {
+        return decimalFormat.format(value);
+    }
+
     private String formatDuration(double duration, long timeStamp) {
-        TimeUnit unit = chooseUnit((long)duration);
+        TimeUnit unit = chooseUnit((long) duration);
         double value = duration / NANOSECONDS.convert(1, unit);
-        return timeStamp > 0 ?
-                String.format("%.4g %s at %3$tD %3$tT", value, abbreviate(unit), new Date(timeStamp)) :
-                String.format("%.4g %s", value, abbreviate(unit));
+
+        return timeStamp > 0 ? String.format("%s %s at %3$tD %3$tT", formatDecimalValue(value), abbreviate(unit),
+                new Date(timeStamp)) : String.format("%s %s", formatDecimalValue(value), abbreviate(unit));
     }
 
     private static TimeUnit chooseUnit(long nanos) {
-        if(SECONDS.convert(nanos, NANOSECONDS) > 0) {
+        if (SECONDS.convert(nanos, NANOSECONDS) > 0) {
             return SECONDS;
         }
 
-        if(MILLISECONDS.convert(nanos, NANOSECONDS) > 0) {
+        if (MILLISECONDS.convert(nanos, NANOSECONDS) > 0) {
             return MILLISECONDS;
         }
 
-        if(MICROSECONDS.convert(nanos, NANOSECONDS) > 0) {
+        if (MICROSECONDS.convert(nanos, NANOSECONDS) > 0) {
             return MICROSECONDS;
         }
 
@@ -169,17 +188,17 @@ public class DurationStatsTracker {
     }
 
     private static String abbreviate(TimeUnit unit) {
-        switch(unit) {
-            case NANOSECONDS:
-                return "ns";
-            case MICROSECONDS:
-                return "\u03bcs"; // Î¼s
-            case MILLISECONDS:
-                return "ms";
-            case SECONDS:
-                return "s";
-            default:
-                return "";
+        switch (unit) {
+        case NANOSECONDS:
+            return "ns";
+        case MICROSECONDS:
+            return "\u03bcs"; // Î¼s
+        case MILLISECONDS:
+            return "ms";
+        case SECONDS:
+            return "s";
+        default:
+            return "";
         }
     }
 }
index 69c94f32a35cfc1e7b1ec9bf71e6287bd88b7acc..2ba16931f18a8f687836415c8da213e2a6cc3bab 100644 (file)
@@ -8,18 +8,21 @@
 
 package org.opendaylight.yangtools.util.concurrent;
 
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.ExecutionList;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListenableFutureTask;
+
 import java.util.concurrent.Callable;
 import java.util.concurrent.Executor;
 import java.util.concurrent.FutureTask;
 
+import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.util.concurrent.ExecutionList;
-import com.google.common.util.concurrent.ListenableFuture;
-
 /**
  * A {@link FutureTask} that also implements the {@link ListenableFuture} interface similar to
  * guava's {@link ListenableFutureTask}. This class differs from ListenableFutureTask in that it
@@ -35,38 +38,91 @@ import com.google.common.util.concurrent.ListenableFuture;
  * listener Runnable would execute in the thread that completed this task, the listener
  * is executed on Executor specified on construction.
  *
+ * Also note that the use of this task may attach some (small) amount of state to the threads
+ * interacting with it. That state will not be detached automatically, but you can use
+ *  {@link #cleanStateForCurrentThread()} to clean it up.
+ *
  * @author Thomas Pantelis
+ * @author Robert Varga
  *
  * @param <V> the Future result value type
  */
 public class AsyncNotifyingListenableFutureTask<V> extends FutureTask<V> implements ListenableFuture<V> {
+    private static final class DelegatingAsyncNotifyingListenableFutureTask<V> extends AsyncNotifyingListenableFutureTask<V> {
+        /**
+         * The executor used to run listener callbacks.
+         */
+        private final Executor listenerExecutor;
+
+        private DelegatingAsyncNotifyingListenableFutureTask(final Callable<V> callable, @Nullable final Executor listenerExecutor) {
+            super(callable);
+            this.listenerExecutor = Preconditions.checkNotNull(listenerExecutor);
+        }
+
+        private DelegatingAsyncNotifyingListenableFutureTask(final Runnable runnable, @Nullable final V result,
+                @Nullable final Executor listenerExecutor) {
+            super(runnable, result);
+            this.listenerExecutor = Preconditions.checkNotNull(listenerExecutor);
+        }
+
+        @Override
+        public void addListener(final Runnable listener, final Executor executor) {
+            // Wrap the listener Runnable in a DelegatingRunnable. If the specified executor is one that
+            // runs tasks in the same thread as the caller submitting the task
+            // (e.g. {@link com.google.common.util.concurrent.MoreExecutors#sameThreadExecutor}) and the
+            // listener is executed from the #done method, then the DelegatingRunnable will detect this
+            // via the ThreadLocal and submit the listener Runnable to the listenerExecutor.
+            //
+            // On the other hand, if this task is already complete, the call to ExecutionList#add in
+            // superclass will execute the listener Runnable immediately and, since the ThreadLocal won't be set,
+            // the DelegatingRunnable will run the listener Runnable inline.
+            super.addListener(new DelegatingRunnable(listener, listenerExecutor), executor);
+        }
+    }
 
-    private static final Logger LOG = LoggerFactory.getLogger( AsyncNotifyingListenableFutureTask.class );
+    private static final class DelegatingRunnable implements Runnable {
+        private final Runnable delegate;
+        private final Executor executor;
+
+        DelegatingRunnable(final Runnable delegate, final Executor executor) {
+            this.delegate = Preconditions.checkNotNull(delegate);
+            this.executor = Preconditions.checkNotNull(executor);
+        }
+
+        @Override
+        public void run() {
+            if (ON_TASK_COMPLETION_THREAD_TL.get().isSet()) {
+                // We're running on the task completion thread so off-load to the executor.
+                LOG.trace("Submitting ListenenableFuture Runnable from thread {} to executor {}",
+                        Thread.currentThread().getName(), executor);
+                executor.execute(delegate);
+            } else {
+                // We're not running on the task completion thread so run the delegate inline.
+                LOG.trace("Executing ListenenableFuture Runnable on this thread: {}",
+                        Thread.currentThread().getName());
+                delegate.run();
+            }
+        }
+    }
+
+    private static final Logger LOG = LoggerFactory.getLogger(AsyncNotifyingListenableFutureTask.class);
 
     /**
      * ThreadLocal used to detect if the task completion thread is running the listeners.
      */
-    private static final ThreadLocal<Boolean> ON_TASK_COMPLETION_THREAD_TL = new ThreadLocal<>();
+    private static final SettableBooleanThreadLocal ON_TASK_COMPLETION_THREAD_TL = new SettableBooleanThreadLocal();
 
     /**
      *  The execution list to hold our listeners.
      */
     private final ExecutionList executionList = new ExecutionList();
 
-    /**
-     * The executor used to run listener callbacks.
-     */
-    private final Executor listenerExecutor;
-
-    private AsyncNotifyingListenableFutureTask( Callable<V> callable, @Nullable Executor listenerExecutor ) {
-        super( callable );
-        this.listenerExecutor = listenerExecutor;
+    private AsyncNotifyingListenableFutureTask(final Callable<V> callable) {
+        super(callable);
     }
 
-    private AsyncNotifyingListenableFutureTask( Runnable runnable, @Nullable V result,
-            @Nullable Executor listenerExecutor ) {
-        super( runnable, result );
-        this.listenerExecutor = listenerExecutor;
+    private AsyncNotifyingListenableFutureTask(final Runnable runnable, @Nullable final V result) {
+        super(runnable, result);
     }
 
     /**
@@ -77,9 +133,13 @@ public class AsyncNotifyingListenableFutureTask<V> extends FutureTask<V> impleme
      * @param listenerExecutor the executor used to run listener callbacks asynchronously.
      *                         If null, no executor is used.
      */
-    public static <V> AsyncNotifyingListenableFutureTask<V> create( Callable<V> callable,
-            @Nullable Executor listenerExecutor ) {
-      return new AsyncNotifyingListenableFutureTask<V>( callable, listenerExecutor );
+    public static <V> AsyncNotifyingListenableFutureTask<V> create(final Callable<V> callable,
+            @Nullable final Executor listenerExecutor) {
+        if (listenerExecutor != null) {
+            return new DelegatingAsyncNotifyingListenableFutureTask<V>(callable, listenerExecutor);
+        } else {
+            return new AsyncNotifyingListenableFutureTask<V>(callable);
+        }
     }
 
     /**
@@ -92,25 +152,26 @@ public class AsyncNotifyingListenableFutureTask<V> extends FutureTask<V> impleme
      * @param listenerExecutor the executor used to run listener callbacks asynchronously.
      *                         If null, no executor is used.
      */
-    public static <V> AsyncNotifyingListenableFutureTask<V> create( Runnable runnable, @Nullable V result,
-            @Nullable Executor listenerExecutor ) {
-      return new AsyncNotifyingListenableFutureTask<V>( runnable, result, listenerExecutor );
+    public static <V> AsyncNotifyingListenableFutureTask<V> create(final Runnable runnable, @Nullable final V result,
+            @Nullable final Executor listenerExecutor) {
+        if (listenerExecutor != null) {
+            return new DelegatingAsyncNotifyingListenableFutureTask<V>(runnable, result, listenerExecutor);
+        } else {
+            return new AsyncNotifyingListenableFutureTask<V>(runnable, result);
+        }
     }
 
     @Override
-    public void addListener( Runnable listener, Executor executor ) {
-        // If a listenerExecutor was specified on construction, wrap the listener Runnable in a
-        // DelegatingRunnable. If the specified executor is one that runs tasks in the same thread
-        // as the caller submitting the task (eg MoreExecutors#sameThreadExecutor) and the
-        // listener is executed from the #done method, then the DelegatingRunnable will detect this
-        // via the ThreadLocal and submit the listener Runnable to the listenerExecutor.
-        //
-        // On the other hand, if this task is already complete, the call to ExecutionList#add below
-        // will execute the listener Runnable immediately and, since the ThreadLocal won't be set,
-        // the DelegatingRunnable will run the listener Runnable inline.
-
-        executionList.add( listenerExecutor == null ? listener :
-            new DelegatingRunnable( listener, listenerExecutor ), executor );
+    public void addListener(@Nonnull final Runnable listener, final Executor executor) {
+        executionList.add(listener, executor);
+    }
+
+    /**
+     * Remove the state which may have attached to the calling thread. If no state
+     * was attached this method does nothing.
+     */
+    public static void cleanStateForCurrentThread() {
+        ON_TASK_COMPLETION_THREAD_TL.remove();
     }
 
     /**
@@ -118,37 +179,13 @@ public class AsyncNotifyingListenableFutureTask<V> extends FutureTask<V> impleme
      */
     @Override
     protected void done() {
-        ON_TASK_COMPLETION_THREAD_TL.set( Boolean.TRUE );
+        final SettableBoolean b = ON_TASK_COMPLETION_THREAD_TL.get();
+        b.set();
+
         try {
             executionList.execute();
         } finally {
-            ON_TASK_COMPLETION_THREAD_TL.remove();
-        }
-    }
-
-    private static class DelegatingRunnable implements Runnable {
-
-        private final Runnable delegate;
-        private final Executor executor;
-
-        DelegatingRunnable( Runnable delegate, Executor executor ) {
-            this.delegate = delegate;
-            this.executor = executor;
-        }
-
-        @Override
-        public void run() {
-            if( ON_TASK_COMPLETION_THREAD_TL.get() == null ) {
-                // We're not running on the task completion thread so run the delegate inline.
-                LOG.trace( "Executing ListenenableFuture Runnable on this thread: {}",
-                        Thread.currentThread().getName() );
-                delegate.run();
-            } else {
-                // We're running on the task completion thread so off-load to the executor.
-                LOG.trace( "Submitting ListenenableFuture Runnable to the listenerExecutor",
-                        Thread.currentThread().getName() );
-                executor.execute( delegate );
-            }
+            b.reset();
         }
     }
 }
index ab010c964de2a7ec32a12c7ef40458068ede6c5e..c44864350e863dc639e307d23fc343efbc38bfc1 100644 (file)
@@ -8,14 +8,14 @@
 
 package org.opendaylight.yangtools.util.concurrent;
 
+import com.google.common.base.Preconditions;
+
 import java.util.concurrent.RejectedExecutionHandler;
 import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicLongFieldUpdater;
 
 import org.opendaylight.yangtools.util.ExecutorServiceUtil;
 
-import com.google.common.base.Preconditions;
-
 /**
  * A RejectedExecutionHandler that delegates to a backing RejectedExecutionHandler and counts the
  * number of rejected tasks.
@@ -23,30 +23,31 @@ import com.google.common.base.Preconditions;
  * @author Thomas Pantelis
  */
 public class CountingRejectedExecutionHandler implements RejectedExecutionHandler {
-
+    private static final AtomicLongFieldUpdater<CountingRejectedExecutionHandler> COUNTER_UPDATER =
+            AtomicLongFieldUpdater.newUpdater(CountingRejectedExecutionHandler.class, "rejectedTaskCounter");
     private final RejectedExecutionHandler delegate;
-    private final AtomicLong rejectedTaskCounter = new AtomicLong();
+    private volatile long rejectedTaskCounter;
 
     /**
      * Constructor.
      *
      * @param delegate the backing RejectedExecutionHandler.
      */
-    public CountingRejectedExecutionHandler( RejectedExecutionHandler delegate ) {
+    public CountingRejectedExecutionHandler( final RejectedExecutionHandler delegate ) {
         this.delegate = Preconditions.checkNotNull( delegate );
     }
 
     @Override
-    public void rejectedExecution( Runnable task, ThreadPoolExecutor executor ) {
-        rejectedTaskCounter.incrementAndGet();
+    public void rejectedExecution( final Runnable task, final ThreadPoolExecutor executor ) {
+        COUNTER_UPDATER.incrementAndGet(this);
         delegate.rejectedExecution( task, executor );
     }
 
     /**
      * Returns the rejected task count.
      */
-    public long getRejectedTaskCount(){
-        return rejectedTaskCounter.get();
+    public long getRejectedTaskCount() {
+        return rejectedTaskCounter;
     }
 
     /**
index 011872d6b138d9edbaf28869524121bd859f0b43..958f2ee5118b265ccdde95cd8ed9891fdde373bb 100644 (file)
@@ -8,11 +8,12 @@
 
 package org.opendaylight.yangtools.util.concurrent;
 
-import static com.google.common.base.Preconditions.checkNotNull;
-
 import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Supplier;
 import com.google.common.util.concurrent.ForwardingListenableFuture;
 import com.google.common.util.concurrent.ListenableFuture;
+
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
@@ -20,6 +21,7 @@ import java.util.concurrent.ExecutorService;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
+import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
 /**
@@ -42,11 +44,35 @@ import javax.annotation.Nullable;
  * from this class override the <code>get</code> methods to check if the ThreadLocal is set. If it is,
  * an ExecutionException is thrown with a custom cause.
  *
+ * Note that the ThreadLocal is not removed automatically, so some state may be left hanging off of
+ * threads which have encountered this class. If you need to clean that state up, use
+ * {@link #cleanStateForCurrentThread()}.
+ *
  * @author Thomas Pantelis
+ * @author Robert Varga
  */
 public class DeadlockDetectingListeningExecutorService extends AsyncNotifyingListeningExecutorService {
-    private final ThreadLocal<Boolean> deadlockDetector = new ThreadLocal<>();
-    private final Function<Void, Exception> deadlockExceptionFunction;
+    /*
+     * We cannot use a static field simply because our API contract allows nesting, which means some
+     * tasks may be submitted to underlay and some to overlay service -- and the two cases need to
+     * be discerned reliably.
+     */
+    private final SettableBooleanThreadLocal deadlockDetector = new SettableBooleanThreadLocal();
+    private final Supplier<Exception> deadlockExceptionFunction;
+
+    // Compatibility wrapper, needs to be removed once the deprecated constructors are gone.
+    private static final class CompatExceptionSupplier implements Supplier<Exception> {
+        private final Function<Void, Exception> function;
+
+        private CompatExceptionSupplier(final Function<Void, Exception> function) {
+            this.function = Preconditions.checkNotNull(function);
+        }
+
+        @Override
+        public Exception get() {
+            return function.apply(null);
+        }
+    }
 
     /**
      * Constructor.
@@ -54,9 +80,11 @@ public class DeadlockDetectingListeningExecutorService extends AsyncNotifyingLis
      * @param delegate the backing ExecutorService.
      * @param deadlockExceptionFunction Function that returns an Exception instance to set as the
      *             cause of the ExecutionException when a deadlock is detected.
+     * @deprecated Use {@link #DeadlockDetectingListeningExecutorService(ExecutorService, Supplier)} instead.
      */
-    public DeadlockDetectingListeningExecutorService( ExecutorService delegate,
-                                          Function<Void,Exception> deadlockExceptionFunction ) {
+    @Deprecated
+    public DeadlockDetectingListeningExecutorService(final ExecutorService delegate,
+            final Function<Void, Exception> deadlockExceptionFunction) {
         this(delegate, deadlockExceptionFunction, null);
     }
 
@@ -68,43 +96,88 @@ public class DeadlockDetectingListeningExecutorService extends AsyncNotifyingLis
      *             cause of the ExecutionException when a deadlock is detected.
      * @param listenableFutureExecutor the executor used to run listener callbacks asynchronously.
      *             If null, no executor is used.
+     * @deprecated Use {@link #DeadlockDetectingListeningExecutorService(ExecutorService, Supplier, Executor)} instead.
      */
-    public DeadlockDetectingListeningExecutorService( ExecutorService delegate,
-                                          Function<Void,Exception> deadlockExceptionFunction,
-                                          @Nullable Executor listenableFutureExecutor ) {
+    @Deprecated
+    public DeadlockDetectingListeningExecutorService(final ExecutorService delegate,
+            final Function<Void, Exception> deadlockExceptionFunction,
+            @Nullable final Executor listenableFutureExecutor) {
         super(delegate, listenableFutureExecutor);
-        this.deadlockExceptionFunction = checkNotNull(deadlockExceptionFunction);
+        this.deadlockExceptionFunction = new CompatExceptionSupplier(deadlockExceptionFunction);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param delegate the backing ExecutorService.
+     * @param deadlockExceptionSupplier Supplier that returns an Exception instance to set as the
+     *             cause of the ExecutionException when a deadlock is detected.
+     */
+    public DeadlockDetectingListeningExecutorService(final ExecutorService delegate,
+            @Nonnull final Supplier<Exception> deadlockExceptionSupplier) {
+        this(delegate, deadlockExceptionSupplier, null);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param delegate the backing ExecutorService.
+     * @param deadlockExceptionSupplier Supplier that returns an Exception instance to set as the
+     *             cause of the ExecutionException when a deadlock is detected.
+     * @param listenableFutureExecutor the executor used to run listener callbacks asynchronously.
+     *             If null, no executor is used.
+     */
+    public DeadlockDetectingListeningExecutorService(final ExecutorService delegate,
+            @Nonnull final Supplier<Exception> deadlockExceptionSupplier,
+            @Nullable final Executor listenableFutureExecutor ) {
+        super(delegate, listenableFutureExecutor);
+        this.deadlockExceptionFunction = Preconditions.checkNotNull(deadlockExceptionSupplier);
     }
 
     @Override
-    public void execute( Runnable command ){
+    public void execute(final Runnable command) {
         getDelegate().execute(wrapRunnable(command));
     }
 
     @Override
-    public <T> ListenableFuture<T> submit( Callable<T> task ){
+    public <T> ListenableFuture<T> submit(final Callable<T> task) {
         return wrapListenableFuture(super.submit(wrapCallable(task)));
     }
 
     @Override
-    public ListenableFuture<?> submit( Runnable task ){
+    public ListenableFuture<?> submit(final Runnable task) {
         return wrapListenableFuture(super.submit(wrapRunnable(task)));
     }
 
     @Override
-    public <T> ListenableFuture<T> submit( Runnable task, T result ){
+    public <T> ListenableFuture<T> submit(final Runnable task, final T result) {
         return wrapListenableFuture(super.submit(wrapRunnable(task), result));
     }
 
+    /**
+     * Remove the state this instance may have attached to the calling thread. If no state
+     * was attached this method does nothing.
+     */
+    public void cleanStateForCurrentThread() {
+        deadlockDetector.remove();
+    }
+
+    private SettableBoolean primeDetector() {
+        final SettableBoolean b = deadlockDetector.get();
+        Preconditions.checkState(!b.isSet(), "Detector for {} has already been primed", this);
+        b.set();
+        return b;
+    }
+
     private Runnable wrapRunnable(final Runnable task) {
         return new Runnable() {
             @Override
             public void run() {
-                deadlockDetector.set(Boolean.TRUE);
+                final SettableBoolean b = primeDetector();
                 try {
                     task.run();
                 } finally {
-                    deadlockDetector.remove();
+                    b.reset();
                 }
             }
         };
@@ -114,17 +187,17 @@ public class DeadlockDetectingListeningExecutorService extends AsyncNotifyingLis
         return new Callable<T>() {
             @Override
             public T call() throws Exception {
-                deadlockDetector.set(Boolean.TRUE);
+                final SettableBoolean b = primeDetector();
                 try {
                     return delagate.call();
                 } finally {
-                    deadlockDetector.remove();
+                    b.reset();
                 }
             }
         };
     }
 
-    private <T> ListenableFuture<T> wrapListenableFuture(final ListenableFuture<T> delegate ) {
+    private <T> ListenableFuture<T> wrapListenableFuture(final ListenableFuture<T> delegate) {
         /*
          * This creates a forwarding Future that overrides calls to get(...) to check, via the
          * ThreadLocal, if the caller is doing a blocking call on a thread from this executor. If
@@ -148,9 +221,9 @@ public class DeadlockDetectingListeningExecutorService extends AsyncNotifyingLis
             }
 
             void checkDeadLockDetectorTL() throws ExecutionException {
-                if (deadlockDetector.get() != null) {
+                if (deadlockDetector.get().isSet()) {
                     throw new ExecutionException("A potential deadlock was detected.",
-                            deadlockExceptionFunction.apply(null));
+                            deadlockExceptionFunction.get());
                 }
             }
         };
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/SettableBoolean.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/SettableBoolean.java
new file mode 100644 (file)
index 0000000..0d584af
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.util.concurrent;
+
+/**
+ * Simple container encapsulating a boolean flag, which can be toggled. It starts
+ * off in the reset state.
+ */
+final class SettableBoolean {
+    private boolean value = false;
+
+    /**
+     * Set the flag to its initial (false) state.
+     */
+    public void reset() {
+        value = false;
+    }
+
+    /**
+     * Set the flag.
+     */
+    public void set() {
+        value = true;
+    }
+
+    /**
+     * Query the flag.
+     *
+     * @return True if the flag has been set since instantiation or last {@link #reset()}.
+     */
+    public boolean isSet() {
+        return value;
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/SettableBooleanThreadLocal.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/SettableBooleanThreadLocal.java
new file mode 100644 (file)
index 0000000..8826f99
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.util.concurrent;
+
+/**
+ * A reusable {@link ThreadLocal} which returns a {@link SettableBoolean}.
+ */
+final class SettableBooleanThreadLocal extends ThreadLocal<SettableBoolean> {
+    @Override
+    protected SettableBoolean initialValue() {
+        return new SettableBoolean();
+    }
+
+    @Override
+    public void set(final SettableBoolean value) {
+        throw new UnsupportedOperationException("Resetting the value is not supported");
+    }
+}
index 38b5d9017fd65968c8464d6dd7b999dc7f760320..853a0aae0ebeb1d08f7145af5b30a55fac2b2328 100644 (file)
@@ -8,11 +8,12 @@
 
 package org.opendaylight.yangtools.util.concurrent;
 
+import com.google.common.annotations.Beta;
+
 import java.util.Collection;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.concurrent.atomic.AtomicLongFieldUpdater;
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
 
 /**
  * A {@link LinkedBlockingQueue} that tracks the largest queue size for debugging.
@@ -22,17 +23,15 @@ import java.util.concurrent.atomic.AtomicLongFieldUpdater;
  * @param <E> the element t.ype
  */
 public class TrackingLinkedBlockingQueue<E> extends LinkedBlockingQueue<E> {
-
+    @SuppressWarnings("rawtypes")
+    private static final AtomicIntegerFieldUpdater<TrackingLinkedBlockingQueue> LARGEST_QUEUE_SIZE_UPDATER = AtomicIntegerFieldUpdater.newUpdater(TrackingLinkedBlockingQueue.class, "largestQueueSize");
     private static final long serialVersionUID = 1L;
 
     /**
      * Holds largestQueueSize, this long field should be only accessed
      * using {@value #LARGEST_QUEUE_SIZE_UPDATER}
      */
-    private volatile long largestQueueSize = 0;
-
-    @SuppressWarnings("rawtypes")
-    private static AtomicLongFieldUpdater<TrackingLinkedBlockingQueue> LARGEST_QUEUE_SIZE_UPDATER = AtomicLongFieldUpdater.newUpdater(TrackingLinkedBlockingQueue.class, "largestQueueSize");
+    private volatile int largestQueueSize = 0;
 
     /**
      * @see LinkedBlockingQueue#LinkedBlockingQueue
@@ -44,26 +43,29 @@ public class TrackingLinkedBlockingQueue<E> extends LinkedBlockingQueue<E> {
     /**
      * @see LinkedBlockingQueue#LinkedBlockingQueue(Collection)
      */
-    public TrackingLinkedBlockingQueue( Collection<? extends E> c ) {
+    public TrackingLinkedBlockingQueue( final Collection<? extends E> c ) {
         super(c);
     }
 
     /**
      * @see LinkedBlockingQueue#LinkedBlockingQueue(int)
      */
-    public TrackingLinkedBlockingQueue( int capacity ) {
+    public TrackingLinkedBlockingQueue( final int capacity ) {
         super(capacity);
     }
 
     /**
      * Returns the largest queue size.
+     *
+     * FIXME: the this return will be changed to int in a future release.
      */
-    public long getLargestQueueSize(){
+    @Beta
+    public long getLargestQueueSize() {
         return largestQueueSize;
     }
 
     @Override
-    public boolean offer( E e, long timeout, TimeUnit unit ) throws InterruptedException {
+    public boolean offer( final E e, final long timeout, final TimeUnit unit ) throws InterruptedException {
         if( super.offer( e, timeout, unit ) ) {
             updateLargestQueueSize();
             return true;
@@ -73,7 +75,7 @@ public class TrackingLinkedBlockingQueue<E> extends LinkedBlockingQueue<E> {
     }
 
     @Override
-    public boolean offer( E e ) {
+    public boolean offer( final E e ) {
         if( super.offer( e ) ) {
             updateLargestQueueSize();
             return true;
@@ -83,20 +85,20 @@ public class TrackingLinkedBlockingQueue<E> extends LinkedBlockingQueue<E> {
     }
 
     @Override
-    public void put( E e ) throws InterruptedException {
+    public void put( final E e ) throws InterruptedException {
         super.put( e );
         updateLargestQueueSize();
     }
 
     @Override
-    public boolean add( E e ) {
+    public boolean add( final E e ) {
         boolean result = super.add( e );
         updateLargestQueueSize();
         return result;
     }
 
     @Override
-    public boolean addAll( Collection<? extends E> c ) {
+    public boolean addAll( final Collection<? extends E> c ) {
         try {
             return super.addAll( c );
         } finally {
@@ -105,10 +107,11 @@ public class TrackingLinkedBlockingQueue<E> extends LinkedBlockingQueue<E> {
     }
 
     private void updateLargestQueueSize() {
-        long size = size();
-        long largest = largestQueueSize;
-        if( size > largest ) {
-            LARGEST_QUEUE_SIZE_UPDATER.compareAndSet(this, largest, size );
-        }
+        final int size = size();
+
+        int largest;
+        do {
+            largest = largestQueueSize;
+        } while (size > largest && !LARGEST_QUEUE_SIZE_UPDATER.compareAndSet(this, largest, size));
     }
 }
index 8c3d8e94880a88bac967df66ae02c183b6c47d48..6a772f4ae4f30326e1c2b3f7f35891621fcf7728 100644 (file)
@@ -31,10 +31,10 @@ import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.RpcService;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
-import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
 import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
 import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
@@ -293,7 +293,7 @@ public class RestconfUtils {
             Document doc = builder.parse(inputStream);
             Element rootElement = doc.getDocumentElement();
             Node<?> domNode = XmlDocumentUtils.toDomNode(rootElement, Optional.of(dataSchema),
-                    Optional.<XmlCodecProvider> absent());
+                    Optional.<XmlCodecProvider> absent(), Optional.of(schemaContext));
             DataObject dataObject = mappingService.dataObjectFromDataDom(path, (CompositeNode) domNode); // getDataFromResponse
             return dataObject;
         } catch (DeserializationException e) {
index 5d2563d683c78f6cee5606fdbedc9b7052f9c760..18969403d01d0f6c663814170d6653fd2128eacb 100644 (file)
@@ -80,7 +80,18 @@ public final class QName implements Immutable, Serializable, Comparable<QName> {
      * @return Cached instance, according to {@link ObjectCache} policy.
      */
     public static QName cachedReference(final QName qname) {
-        return CACHE.getReference(qname);
+        // We also want to make sure we keep the QNameModule cached
+        final QNameModule myMod = qname.getModule();
+        final QNameModule cacheMod = QNameModule.cachedReference(myMod);
+
+        final QName what;
+        if (cacheMod == myMod) {
+            what = qname;
+        } else {
+            what = QName.create(cacheMod, qname.localName);
+        }
+
+        return CACHE.getReference(what);
     }
 
     /**
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/AbstractJSONCodec.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/AbstractJSONCodec.java
new file mode 100644 (file)
index 0000000..9464653
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import com.google.common.base.Preconditions;
+
+import org.opendaylight.yangtools.concepts.Codec;
+import org.opendaylight.yangtools.yang.data.api.codec.BooleanCodec;
+import org.opendaylight.yangtools.yang.data.api.codec.DecimalCodec;
+import org.opendaylight.yangtools.yang.data.api.codec.Int16Codec;
+import org.opendaylight.yangtools.yang.data.api.codec.Int32Codec;
+import org.opendaylight.yangtools.yang.data.api.codec.Int64Codec;
+import org.opendaylight.yangtools.yang.data.api.codec.Int8Codec;
+import org.opendaylight.yangtools.yang.data.api.codec.Uint16Codec;
+import org.opendaylight.yangtools.yang.data.api.codec.Uint32Codec;
+import org.opendaylight.yangtools.yang.data.api.codec.Uint64Codec;
+import org.opendaylight.yangtools.yang.data.api.codec.Uint8Codec;
+import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
+
+/**
+ * Abstract base implementation of {@link JSONCodec}, which wraps a {@link TypeDefinitionAwareCodec}.
+ *
+ * @param <T> Deserialized objec type
+ */
+abstract class AbstractJSONCodec<T> implements JSONCodec<T> {
+    private final Codec<String, T> codec;
+
+    protected AbstractJSONCodec(final Codec<String, T> codec) {
+        this.codec = Preconditions.checkNotNull(codec);
+    }
+
+    /**
+     * Create a proper JSONCodec based on the underlying codec type
+     * @param codec underlying codec
+     * @return A JSONCodec instance
+     */
+    public static <T> JSONCodec<T> create(final Codec<String, T> codec) {
+        if (codec instanceof BooleanCodec || codec instanceof DecimalCodec ||
+                codec instanceof Int8Codec || codec instanceof Int16Codec ||
+                codec instanceof Int32Codec || codec instanceof Int64Codec ||
+                codec instanceof Uint8Codec || codec instanceof Uint16Codec ||
+                codec instanceof Uint32Codec || codec instanceof Uint64Codec) {
+            return new UnquotedJSONCodec<>(codec);
+        }
+
+        return new QuotedJSONCodec<>(codec);
+    }
+
+    @Override
+    public final T deserialize(final String input) {
+        return codec.deserialize(input);
+    }
+
+    @Override
+    public final String serialize(final T input) {
+        return codec.serialize(input);
+    }
+}
index c2997a84d74814feae68fec7fc76adc9122e66b7..ee2541d8e651888fd4062db56828e0046b3a8168 100644 (file)
@@ -13,16 +13,13 @@ import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Multimap;
-
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Deque;
 import java.util.List;
 import java.util.Map.Entry;
-
 import javax.annotation.Nonnull;
-
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
@@ -193,7 +190,7 @@ class CompositeNodeDataWithSchema extends AbstractNodeDataWithSchema {
      * node is found then it is returned, else null.
      */
     AugmentationSchema findCorrespondingAugment(final DataSchemaNode parent, final DataSchemaNode child) {
-        if (parent instanceof AugmentationTarget) {
+        if (parent instanceof AugmentationTarget && !((parent instanceof ChoiceCaseNode) || (parent instanceof ChoiceNode))) {
             for (AugmentationSchema augmentation : ((AugmentationTarget) parent).getAvailableAugmentations()) {
                 DataSchemaNode childInAugmentation = augmentation.getDataChildByName(child.getQName());
                 if (childInAugmentation != null) {
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONCodec.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONCodec.java
new file mode 100644 (file)
index 0000000..aa52259
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import org.opendaylight.yangtools.concepts.Codec;
+
+interface JSONCodec<T> extends Codec<String, T> {
+    boolean needQuotes();
+}
similarity index 55%
rename from yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/CodecFactory.java
rename to yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONCodecFactory.java
index dba5ed7d24638c4f757622911725a75ff8d84ff9..8ee9517ec2db90f976aa64e5b26a883abe3a7c4e 100644 (file)
@@ -8,12 +8,11 @@
 package org.opendaylight.yangtools.yang.data.codec.gson;
 
 import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
 
-import org.opendaylight.yangtools.concepts.Codec;
-import org.opendaylight.yangtools.yang.data.api.codec.LeafrefCodec;
 import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
@@ -24,35 +23,30 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * This class is implementation-internal and subject to change. Please do not use it.
+ * Factory for creating JSON equivalents of codecs. Each instance of this object is bound to
+ * a particular {@link SchemaContext}, but can be reused by multiple {@link JSONNormalizedNodeStreamWriter}s.
  */
 @Beta
-final class CodecFactory {
-    private static final Logger LOG = LoggerFactory.getLogger(CodecFactory.class);
-    private static final Codec<?, ?> LEAFREF_DEFAULT_CODEC = new LeafrefCodec<String>() {
+public final class JSONCodecFactory {
+    private static final Logger LOG = LoggerFactory.getLogger(JSONCodecFactory.class);
+    private static final JSONCodec<Object> LEAFREF_DEFAULT_CODEC = new JSONLeafrefCodec();
+    private static final JSONCodec<Object> NULL_CODEC = new JSONCodec<Object>() {
         @Override
-        public String serialize(final Object data) {
-            return String.valueOf(data);
+        public Object deserialize(final String input) {
+            return null;
         }
 
         @Override
-        public Object deserialize(final String data) {
-            return data;
-        }
-    };
-    private static final Codec<?, ?> NULL_CODEC = new Codec<Object, Object>() {
-        @Override
-        public Object deserialize(final Object input) {
+        public String serialize(final Object input) {
             return null;
         }
 
         @Override
-        public Object serialize(final Object input) {
-            return null;
+        public boolean needQuotes() {
+            return false;
         }
     };
 
-
     private static TypeDefinition<?> resolveBaseTypeFrom(final TypeDefinition<?> type) {
         TypeDefinition<?> superType = type;
         while (superType.getBaseType() != null) {
@@ -61,17 +55,18 @@ final class CodecFactory {
         return superType;
     }
 
-    private final LoadingCache<TypeDefinition<?>, Codec<?, ?>> codecs =
-            CacheBuilder.newBuilder().softValues().build(new CacheLoader<TypeDefinition<?>, Codec<?, ?>>() {
+    private final LoadingCache<TypeDefinition<?>, JSONCodec<Object>> codecs =
+            CacheBuilder.newBuilder().softValues().build(new CacheLoader<TypeDefinition<?>, JSONCodec<Object>>() {
+        @SuppressWarnings("unchecked")
         @Override
-        public Codec<?, ?> load(final TypeDefinition<?> key) throws Exception {
+        public JSONCodec<Object> load(final TypeDefinition<?> key) throws Exception {
             final TypeDefinition<?> type = resolveBaseTypeFrom(key);
 
             if (type instanceof InstanceIdentifierType) {
-                return iidCodec;
+                return (JSONCodec<Object>) iidCodec;
             }
             if (type instanceof IdentityrefType) {
-                return idrefCodec;
+                return (JSONCodec<Object>) idrefCodec;
             }
             if (type instanceof LeafrefTypeDefinition) {
                 return LEAFREF_DEFAULT_CODEC;
@@ -83,24 +78,35 @@ final class CodecFactory {
                 return NULL_CODEC;
             }
 
-            return codec;
+            return AbstractJSONCodec.create(codec);
         }
     });
 
-    private final Codec<?, ?> iidCodec;
-    private final Codec<?, ?> idrefCodec;
+    private final SchemaContext schemaContext;
+    private final JSONCodec<?> iidCodec;
+    private final JSONCodec<?> idrefCodec;
 
-    private CodecFactory(final SchemaContext context) {
+    private JSONCodecFactory(final SchemaContext context) {
+        this.schemaContext = Preconditions.checkNotNull(context);
         iidCodec = new JSONStringInstanceIdentifierCodec(context);
         idrefCodec = new JSONStringIdentityrefCodec(context);
     }
 
-    public static CodecFactory create(final SchemaContext context) {
-        return new CodecFactory(context);
+    /**
+     * Instantiate a new codec factory attached to a particular context.
+     *
+     * @param context SchemaContext to which the factory should be bound
+     * @return A codec factory instance.
+     */
+    public static JSONCodecFactory create(final SchemaContext context) {
+        return new JSONCodecFactory(context);
+    }
+
+    SchemaContext getSchemaContext() {
+        return schemaContext;
     }
 
-    @SuppressWarnings("unchecked")
-    public final Codec<Object, Object> codecFor(final TypeDefinition<?> typeDefinition) {
-        return (Codec<Object, Object>) codecs.getUnchecked(typeDefinition);
+    JSONCodec<Object> codecFor(final TypeDefinition<?> typeDefinition) {
+        return codecs.getUnchecked(typeDefinition);
     }
 }
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONLeafrefCodec.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONLeafrefCodec.java
new file mode 100644 (file)
index 0000000..5613433
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import org.opendaylight.yangtools.yang.data.api.codec.LeafrefCodec;
+
+final class JSONLeafrefCodec implements JSONCodec<Object>, LeafrefCodec<String> {
+    @Override
+    public Object deserialize(final String input) {
+        return input;
+    }
+
+    @Override
+    public String serialize(final Object input) {
+        return String.valueOf(input);
+    }
+
+    @Override
+    public boolean needQuotes() {
+        return true;
+    }
+}
\ No newline at end of file
index c7755b83bd42ac282f309014ff4d20963a4873e3..643e9de9ad041f9b2c4c3c02bf6e3d3317061295 100644 (file)
@@ -7,30 +7,24 @@
  */
 package org.opendaylight.yangtools.yang.data.codec.gson;
 
+import com.google.common.base.CharMatcher;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableSet;
 import com.google.gson.stream.JsonWriter;
 
 import java.io.IOException;
 import java.io.Writer;
-import java.math.BigDecimal;
-import java.math.BigInteger;
 import java.net.URI;
-import java.util.ArrayDeque;
-import java.util.Collection;
-import java.util.Deque;
 
-import org.opendaylight.yangtools.concepts.Codec;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
 import org.opendaylight.yangtools.yang.data.impl.codec.SchemaTracker;
 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 
@@ -42,60 +36,25 @@ import org.opendaylight.yangtools.yang.model.api.SchemaPath;
  * FIXME: rewrite this in terms of {@link JsonWriter}.
  */
 public class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWriter {
+    /**
+     * RFC6020 deviation: we are not required to emit empty containers unless they
+     * are marked as 'presence'.
+     */
+    private static final boolean DEFAULT_EMIT_EMPTY_CONTAINERS = true;
 
-    private static enum NodeType {
-        OBJECT,
-        LIST,
-        OTHER,
-    }
-
-    private static class TypeInfo {
-        private boolean hasAtLeastOneChild = false;
-        private final NodeType type;
-        private final URI uri;
-
-        public TypeInfo(final NodeType type, final URI uri) {
-            this.type = type;
-            this.uri = uri;
-        }
-
-        public void setHasAtLeastOneChild(final boolean hasChildren) {
-            this.hasAtLeastOneChild = hasChildren;
-        }
-
-        public NodeType getType() {
-            return type;
-        }
-
-        public URI getNamespace() {
-            return uri;
-        }
-
-        public boolean hasAtLeastOneChild() {
-            return hasAtLeastOneChild;
-        }
-    }
+    /**
+     * Matcher used to check if a string needs to be escaped.
+     */
+    private static final CharMatcher QUOTES_OR_BACKSLASH = CharMatcher.anyOf("\\\"");
 
-    private static final Collection<Class<?>> NUMERIC_CLASSES =
-            ImmutableSet.<Class<?>>of(Byte.class, Short.class, Integer.class, Long.class, BigInteger.class, BigDecimal.class);
-    private final Deque<TypeInfo> stack = new ArrayDeque<>();
-    private final SchemaContext schemaContext;
-    private final CodecFactory codecs;
     private final SchemaTracker tracker;
+    private final JSONCodecFactory codecs;
     private final Writer writer;
     private final String indent;
+    private JSONStreamWriterContext context;
 
-    private int currentDepth = 0;
-    private URI currentNamespace;
-
-    private JSONNormalizedNodeStreamWriter(final SchemaContext schemaContext,
-            final Writer writer, final int indentSize) {
-        this(schemaContext, SchemaPath.ROOT, writer, null, indentSize);
-    }
-
-    private JSONNormalizedNodeStreamWriter(final SchemaContext schemaContext, final SchemaPath path,
+    private JSONNormalizedNodeStreamWriter(final JSONCodecFactory codecFactory, final SchemaPath path,
             final Writer writer, final URI initialNs, final int indentSize) {
-        this.schemaContext = Preconditions.checkNotNull(schemaContext);
         this.writer = Preconditions.checkNotNull(writer);
 
         Preconditions.checkArgument(indentSize >= 0, "Indent size must be non-negative");
@@ -104,10 +63,9 @@ public class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWrite
         } else {
             indent = null;
         }
-        this.codecs = CodecFactory.create(schemaContext);
-        this.tracker = SchemaTracker.create(schemaContext, path);
-
-        this.currentNamespace = initialNs;
+        this.codecs = Preconditions.checkNotNull(codecFactory);
+        this.tracker = SchemaTracker.create(codecFactory.getSchemaContext(), path);
+        this.context = new JSONStreamWriterRootContext(initialNs);
     }
 
     /**
@@ -118,30 +76,33 @@ public class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWrite
      * @return A stream writer instance
      */
     public static NormalizedNodeStreamWriter create(final SchemaContext schemaContext, final Writer writer) {
-        return new JSONNormalizedNodeStreamWriter(schemaContext, writer, 0);
+        return new JSONNormalizedNodeStreamWriter(JSONCodecFactory.create(schemaContext), SchemaPath.ROOT, writer, null, 0);
     }
 
     /**
      * Create a new stream writer, which writes to the specified {@link Writer}.
      *
      * @param schemaContext Schema context
+     * @param path Root schemapath
      * @param writer Output writer
      * @return A stream writer instance
      */
-    public static NormalizedNodeStreamWriter create(final SchemaContext schemaContext, final SchemaPath path,final Writer writer) {
-        return new JSONNormalizedNodeStreamWriter(schemaContext, path, writer, null, 0);
+    public static NormalizedNodeStreamWriter create(final SchemaContext schemaContext, final SchemaPath path, final Writer writer) {
+        return new JSONNormalizedNodeStreamWriter(JSONCodecFactory.create(schemaContext), path, writer, null, 0);
     }
 
     /**
      * Create a new stream writer, which writes to the specified {@link Writer}.
      *
      * @param schemaContext Schema context
+     * @param path Root schemapath
      * @param writer Output writer
      * @param initialNs Initial namespace
      * @return A stream writer instance
      */
-    public static NormalizedNodeStreamWriter create(final SchemaContext schemaContext, final SchemaPath path,final URI initialNs, final Writer writer) {
-        return new JSONNormalizedNodeStreamWriter(schemaContext, path, writer, initialNs, 0);
+    public static NormalizedNodeStreamWriter create(final SchemaContext schemaContext, final SchemaPath path,
+            final URI initialNs, final Writer writer) {
+        return new JSONNormalizedNodeStreamWriter(JSONCodecFactory.create(schemaContext), path, writer, initialNs, 0);
     }
 
     /**
@@ -153,118 +114,99 @@ public class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWrite
      * @return A stream writer instance
      */
     public static NormalizedNodeStreamWriter create(final SchemaContext schemaContext, final Writer writer, final int indentSize) {
-        return new JSONNormalizedNodeStreamWriter(schemaContext, writer, indentSize);
+        return new JSONNormalizedNodeStreamWriter(JSONCodecFactory.create(schemaContext), SchemaPath.ROOT, writer, null, indentSize);
+    }
+
+    /**
+     * Create a new stream writer, which writes to the specified output stream. The codec factory
+     * can be reused between multiple writers.
+     *
+     * @param codecFactor JSON codec factory
+     * @param writer Output writer
+     * @param indentSize indentation size
+     * @return A stream writer instance
+     */
+    public static NormalizedNodeStreamWriter create(final JSONCodecFactory codecFactory, final Writer writer, final int indentSize) {
+        return new JSONNormalizedNodeStreamWriter(codecFactory, SchemaPath.ROOT, writer, null, indentSize);
     }
 
     @Override
     public void leafNode(final NodeIdentifier name, final Object value) throws IOException {
         final LeafSchemaNode schema = tracker.leafNode(name);
-        final Codec<Object, Object> codec = codecs.codecFor(schema.getType());
+        final JSONCodec<Object> codec = codecs.codecFor(schema.getType());
 
-        separateElementFromPreviousElement();
-        writeJsonIdentifier(name);
-        currentNamespace = stack.peek().getNamespace();
-        writeValue(codec.serialize(value));
-        separateNextSiblingsWithComma();
+        context.emittingChild(codecs.getSchemaContext(), writer, indent);
+        context.writeJsonIdentifier(codecs.getSchemaContext(), writer, name.getNodeType());
+        writeValue(codec.serialize(value), codec.needQuotes());
     }
 
     @Override
     public void startLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
         tracker.startLeafSet(name);
-
-        separateElementFromPreviousElement();
-        stack.push(new TypeInfo(NodeType.LIST, name.getNodeType().getNamespace()));
-        writeJsonIdentifier(name);
-        writeStartList();
-        indentRight();
+        context = new JSONStreamWriterListContext(context, name);
     }
 
     @Override
     public void leafSetEntryNode(final Object value) throws IOException {
         final LeafListSchemaNode schema = tracker.leafSetEntryNode();
-        final Codec<Object, Object> codec = codecs.codecFor(schema.getType());
+        final JSONCodec<Object> codec = codecs.codecFor(schema.getType());
 
-        separateElementFromPreviousElement();
-        writeValue(codec.serialize(value));
-        separateNextSiblingsWithComma();
+        context.emittingChild(codecs.getSchemaContext(), writer, indent);
+        writeValue(codec.serialize(value), codec.needQuotes());
     }
 
+    /*
+     * Warning suppressed due to static final constant which triggers a warning
+     * for the call to schema.isPresenceContainer().
+     */
+    @SuppressWarnings("unused")
     @Override
     public void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
-        tracker.startContainerNode(name);
-
-        separateElementFromPreviousElement();
-        stack.push(new TypeInfo(NodeType.OBJECT, name.getNodeType().getNamespace()));
-        writeJsonIdentifier(name);
-        writeStartObject();
-        indentRight();
+        final ContainerSchemaNode schema = tracker.startContainerNode(name);
+        context = new JSONStreamWriterNamedObjectContext(context, name, DEFAULT_EMIT_EMPTY_CONTAINERS || schema.isPresenceContainer());
     }
 
     @Override
     public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) throws IOException {
         tracker.startList(name);
-
-        separateElementFromPreviousElement();
-        stack.push(new TypeInfo(NodeType.LIST, name.getNodeType().getNamespace()));
-        writeJsonIdentifier(name);
-        writeStartList();
-        indentRight();
+        context = new JSONStreamWriterListContext(context, name);
     }
 
     @Override
     public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) throws IOException {
         tracker.startListItem(name);
-
-        separateElementFromPreviousElement();
-        stack.push(new TypeInfo(NodeType.OBJECT, name.getNodeType().getNamespace()));
-        writeStartObject();
-        indentRight();
+        context = new JSONStreamWriterObjectContext(context, name, DEFAULT_EMIT_EMPTY_CONTAINERS);
     }
 
     @Override
     public void startMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
         tracker.startList(name);
-
-        separateElementFromPreviousElement();
-        stack.push(new TypeInfo(NodeType.LIST, name.getNodeType().getNamespace()));
-        writeJsonIdentifier(name);
-        writeStartList();
-        indentRight();
+        context = new JSONStreamWriterListContext(context, name);
     }
 
     @Override
     public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint)
             throws IOException {
         tracker.startListItem(identifier);
-        separateElementFromPreviousElement();
-        stack.push(new TypeInfo(NodeType.OBJECT, identifier.getNodeType().getNamespace()));
-
-
-        writeStartObject();
-        indentRight();
+        context = new JSONStreamWriterObjectContext(context, identifier, DEFAULT_EMIT_EMPTY_CONTAINERS);
     }
 
     @Override
     public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
         tracker.startList(name);
-
-        separateElementFromPreviousElement();
-        stack.push(new TypeInfo(NodeType.LIST, name.getNodeType().getNamespace()));
-        writeJsonIdentifier(name);
-        writeStartList();
-        indentRight();
+        context = new JSONStreamWriterListContext(context, name);
     }
 
     @Override
-    public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) throws IllegalArgumentException {
+    public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) {
         tracker.startChoiceNode(name);
-        handleInvisibleNode(name.getNodeType().getNamespace());
+        context = new JSONStreamWriterInvisibleContext(context);
     }
 
     @Override
-    public void startAugmentationNode(final AugmentationIdentifier identifier) throws IllegalArgumentException {
+    public void startAugmentationNode(final AugmentationIdentifier identifier) {
         tracker.startAugmentationNode(identifier);
-        handleInvisibleNode(currentNamespace);
+        context = new JSONStreamWriterInvisibleContext(context);
     }
 
     @Override
@@ -272,114 +214,44 @@ public class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWrite
         final AnyXmlSchemaNode schema = tracker.anyxmlNode(name);
         // FIXME: should have a codec based on this :)
 
-        separateElementFromPreviousElement();
-        writeJsonIdentifier(name);
-        currentNamespace = stack.peek().getNamespace();
-        writeValue(value);
-        separateNextSiblingsWithComma();
+        context.emittingChild(codecs.getSchemaContext(), writer, indent);
+        context.writeJsonIdentifier(codecs.getSchemaContext(), writer, name.getNodeType());
+        writeValue(String.valueOf(value), true);
     }
 
     @Override
     public void endNode() throws IOException {
         tracker.endNode();
-
-        final TypeInfo t = stack.pop();
-        switch (t.getType()) {
-        case LIST:
-            indentLeft();
-            newLine();
-            writer.append(']');
-            break;
-        case OBJECT:
-            indentLeft();
-            newLine();
-            writer.append('}');
-            break;
-        default:
-            break;
-        }
-
-        currentNamespace = stack.isEmpty() ? null : stack.peek().getNamespace();
-        separateNextSiblingsWithComma();
-    }
-
-    private void separateElementFromPreviousElement() throws IOException {
-        if (!stack.isEmpty() && stack.peek().hasAtLeastOneChild()) {
-            writer.append(',');
-        }
-        newLine();
+        context = context.endNode(codecs.getSchemaContext(), writer, indent);
     }
 
-    private void newLine() throws IOException {
-        if (indent != null) {
-            writer.append('\n');
+    private void writeValue(final String str, final boolean needQuotes) throws IOException {
+        if (needQuotes) {
+            writer.append('"');
 
-            for (int i = 0; i < currentDepth; i++) {
-                writer.append(indent);
+            final int needEscape = QUOTES_OR_BACKSLASH.countIn(str);
+            if (needEscape != 0) {
+                final char[] escaped = new char[str.length() + needEscape];
+                int offset = 0;
+
+                for (int i = 0; i < str.length(); i++) {
+                    final char c = str.charAt(i);
+                    if (QUOTES_OR_BACKSLASH.matches(c)) {
+                        escaped[offset++] = '\\';
+                    }
+                    escaped[offset++] = c;
+                }
+                writer.write(escaped);
+            } else {
+                writer.append(str);
             }
-        }
-    }
 
-    private void separateNextSiblingsWithComma() {
-        if (!stack.isEmpty()) {
-            stack.peek().setHasAtLeastOneChild(true);
-        }
-    }
-
-    /**
-     * Invisible nodes have to be also pushed to stack because of pairing of start*() and endNode() methods. Information
-     * about child existing (due to printing comma) has to be transfered to invisible node.
-     */
-    private void handleInvisibleNode(final URI uri) {
-        TypeInfo typeInfo = new TypeInfo(NodeType.OTHER, uri);
-        typeInfo.setHasAtLeastOneChild(stack.peek().hasAtLeastOneChild());
-        stack.push(typeInfo);
-    }
-
-    private void writeStartObject() throws IOException {
-        writer.append('{');
-    }
-
-    private void writeStartList() throws IOException {
-        writer.append('[');
-    }
-
-    private void writeModulName(final URI namespace) throws IOException {
-        if (this.currentNamespace == null || namespace != this.currentNamespace) {
-            Module module = schemaContext.findModuleByNamespaceAndRevision(namespace, null);
-            writer.append(module.getName());
-            writer.append(':');
-            currentNamespace = namespace;
-        }
-    }
-
-    private void writeValue(final Object value) throws IOException {
-        final String str = String.valueOf(value);
-
-        if (!NUMERIC_CLASSES.contains(value.getClass())) {
-            writer.append('"');
-            writer.append(str);
             writer.append('"');
         } else {
             writer.append(str);
         }
     }
 
-    private void writeJsonIdentifier(final NodeIdentifier name) throws IOException {
-        writer.append('"');
-        writeModulName(name.getNodeType().getNamespace());
-        writer.append(name.getNodeType().getLocalName());
-        writer.append("\":");
-    }
-
-    private void indentRight() {
-        currentDepth++;
-    }
-
-    private void indentLeft() {
-        currentDepth--;
-    }
-
     @Override
     public void flush() throws IOException {
         writer.flush();
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterContext.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterContext.java
new file mode 100644 (file)
index 0000000..1a5181a
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import com.google.common.base.Preconditions;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.net.URI;
+
+import javax.annotation.Nonnull;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Abstract base class for a single level of {@link JSONNormalizedNodeStreamWriter}
+ * recursion. Provides the base API towards the writer, which is then specialized
+ * by subclasses.
+ */
+abstract class JSONStreamWriterContext {
+    private final JSONStreamWriterContext parent;
+    private final boolean mandatory;
+    private final int depth;
+    private boolean emittedMyself = false;
+    private boolean haveChild = false;
+
+    /**
+     * Construct a new context.
+     *
+     * @param parent Parent context, usually non-null.
+     * @param mandatory Mandatory flag. If set to true, the corresponding node
+     *                  will be emitted even if it has no children.
+     */
+    protected JSONStreamWriterContext(final JSONStreamWriterContext parent, final boolean mandatory) {
+        this.mandatory = mandatory;
+        this.parent = parent;
+
+        if (parent != null) {
+            depth = parent.depth + 1;
+        } else {
+            depth = 0;
+        }
+    }
+
+    /**
+     * Write a JSON node identifier, optionally prefixing it with the module name
+     * corresponding to its namespace.
+     *
+     * @param schema Schema context
+     * @param writer Output writer
+     * @param qname Namespace/name tuple
+     * @throws IOException when the writer reports it
+     */
+    protected final void writeJsonIdentifier(final SchemaContext schema, final Writer writer, final QName qname) throws IOException {
+        writer.append('"');
+
+        // Prepend module name if namespaces do not match
+        final URI ns = qname.getNamespace();
+        if (!ns.equals(getNamespace())) {
+            final Module module = schema.findModuleByNamespaceAndRevision(ns, null);
+            Preconditions.checkArgument(module != null, "Could not find module for namespace {}", ns);
+
+            writer.append(module.getName());
+            writer.append(':');
+        }
+
+        writer.append(qname.getLocalName());
+        writer.append("\":");
+    }
+
+    /**
+     * Return the namespace associated with current node.
+     *
+     * @return Namespace as URI
+     */
+    protected abstract @Nonnull URI getNamespace();
+
+    /**
+     * Emit the start of an element.
+     *
+     * @param schema Schema context
+     * @param writer Output writer
+     * @throws IOException
+     */
+    protected abstract void emitStart(final SchemaContext schema, final Writer writer) throws IOException;
+
+    /**
+     * Emit the end of an element.
+     *
+     * @param schema Schema context
+     * @param writer Output writer
+     * @throws IOException
+     */
+    protected abstract void emitEnd(final Writer writer) throws IOException;
+
+    private final void emitMyself(final SchemaContext schema, final Writer writer, final String indent) throws IOException {
+        if (!emittedMyself) {
+            if (parent != null) {
+                parent.emittingChild(schema, writer, indent);
+            }
+
+            emitStart(schema, writer);
+            emittedMyself = true;
+        }
+    }
+
+    /**
+     * Invoked whenever a child node is being emitted. Checks whether this node has
+     * been emitted, and takes care of that if necessary. Also makes sure separator
+     * is emitted before a second and subsequent child.
+     *
+     * @param schema Schema context
+     * @param writer Output writer
+     * @param indent Indentation string
+     * @throws IOException when writer reports it
+     */
+    final void emittingChild(final SchemaContext schema, final Writer writer, final String indent) throws IOException {
+        emitMyself(schema, writer, indent);
+        if (haveChild) {
+            writer.append(',');
+        }
+
+        if (indent != null) {
+            writer.append('\n');
+
+            for (int i = 0; i < depth; i++) {
+                writer.append(indent);
+            }
+        }
+        haveChild = true;
+    }
+
+    /**
+     * Invoked by the writer when it is leaving this node. Checks whether this node
+     * needs to be emitted and takes of that if necessary.
+     *
+     * @param schema Schema context
+     * @param writer Output writer
+     * @param indent Indentation string
+     * @return Parent node context
+     * @throws IOException when writer reports it
+     * @throws IllegalArgumentException if this node cannot be ended (e.g. root)
+     */
+    final JSONStreamWriterContext endNode(final SchemaContext schema, final Writer writer, final String indent) throws IOException {
+        if (!emittedMyself && mandatory) {
+            emitMyself(schema, writer, indent);
+        }
+
+        if (emittedMyself) {
+            emitEnd(writer);
+        }
+        return parent;
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterInvisibleContext.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterInvisibleContext.java
new file mode 100644 (file)
index 0000000..7f22c19
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import com.google.common.base.Preconditions;
+
+import java.io.Writer;
+
+/**
+ * A virtual recursion level in {@link JSONNormalizedNodeStreamWriter}, used for nodes
+ * which are not emitted in the JSON representation.
+ */
+final class JSONStreamWriterInvisibleContext extends JSONStreamWriterURIContext {
+    JSONStreamWriterInvisibleContext(final JSONStreamWriterContext parent) {
+        super(Preconditions.checkNotNull(parent), parent.getNamespace());
+    }
+
+    @Override
+    protected void emitEnd(final Writer writer) {
+        // No-op
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterListContext.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterListContext.java
new file mode 100644 (file)
index 0000000..ed7bd09
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import com.google.common.base.Preconditions;
+import java.io.IOException;
+import java.io.Writer;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * A single recursion level of {@link JSONNormalizedNodeStreamWriter} representing
+ * a list.
+ */
+final class JSONStreamWriterListContext extends JSONStreamWriterQNameContext {
+    protected JSONStreamWriterListContext(final JSONStreamWriterContext parent, final NodeIdentifier id) {
+        super(Preconditions.checkNotNull(parent), id.getNodeType(), false);
+    }
+
+    @Override
+    protected void emitStart(final SchemaContext schema, final Writer writer) throws IOException {
+        writeJsonIdentifier(schema, writer, getQName());
+        writer.append('[');
+    }
+
+    @Override
+    protected void emitEnd(final Writer writer) throws IOException {
+        writer.append(']');
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterNamedObjectContext.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterNamedObjectContext.java
new file mode 100644 (file)
index 0000000..f5e5d48
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * A recursion level of {@link JSONNormalizedNodeStreamWriter}, which represents
+ * a JSON object which has to be prefixed with its identifier -- such as a
+ * container.
+ */
+final class JSONStreamWriterNamedObjectContext extends JSONStreamWriterObjectContext {
+    protected JSONStreamWriterNamedObjectContext(final JSONStreamWriterContext parent, final PathArgument arg, final boolean mandatory) {
+        super(parent, arg, mandatory);
+    }
+
+    @Override
+    protected void emitStart(final SchemaContext schema, final Writer writer) throws IOException {
+        writeJsonIdentifier(schema, writer, getQName());
+        super.emitStart(schema, writer);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterObjectContext.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterObjectContext.java
new file mode 100644 (file)
index 0000000..d12f044
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import com.google.common.base.Preconditions;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * A recursion level of {@link JSONNormalizedNodeStreamWriter}, which represents
+ * a JSON object which does not have to be prefixed with its identifier -- such
+ * as when it is in a containing list.
+ */
+class JSONStreamWriterObjectContext extends JSONStreamWriterQNameContext {
+    protected JSONStreamWriterObjectContext(final JSONStreamWriterContext parent, final PathArgument arg, final boolean mandatory) {
+        super(Preconditions.checkNotNull(parent), arg.getNodeType(), mandatory);
+    }
+
+    @Override
+    protected void emitStart(final SchemaContext schema, final Writer writer) throws IOException {
+        writer.append('{');
+    }
+
+    @Override
+    protected void emitEnd(final Writer writer) throws IOException {
+        writer.append('}');
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterQNameContext.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterQNameContext.java
new file mode 100644 (file)
index 0000000..ad4ad6b
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import com.google.common.base.Preconditions;
+
+import java.net.URI;
+
+import org.opendaylight.yangtools.yang.common.QName;
+
+/**
+ * Abstract base class for {@link JSONNormalizedNodeStreamWriter} recursion
+ * levels which emit a QName-identified node.
+ */
+abstract class JSONStreamWriterQNameContext extends JSONStreamWriterContext {
+    private final QName qname;
+
+    protected JSONStreamWriterQNameContext(final JSONStreamWriterContext parent, final QName qname, final boolean mandatory) {
+        super(parent, mandatory);
+        this.qname = Preconditions.checkNotNull(qname);
+    }
+
+    /**
+     * Returns the node's identifier as a QName.
+     *
+     * @return QName identifier
+     */
+    protected final QName getQName() {
+        return qname;
+    }
+
+    @Override
+    protected final URI getNamespace() {
+        return qname.getNamespace();
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterRootContext.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterRootContext.java
new file mode 100644 (file)
index 0000000..36c3ff3
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import java.io.Writer;
+import java.net.URI;
+
+/**
+ * The root node of a particular {@link JSONNormalizedNodeStreamWriter} instance.
+ * It holds the base namespace and can never be removed from the stack.
+ */
+final class JSONStreamWriterRootContext extends JSONStreamWriterURIContext {
+    JSONStreamWriterRootContext(final URI namespace) {
+        super(null, namespace);
+    }
+
+    @Override
+    protected void emitEnd(final Writer writer) {
+        throw new IllegalArgumentException("Top-level node reached");
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterURIContext.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterURIContext.java
new file mode 100644 (file)
index 0000000..06c32bf
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.net.URI;
+
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Abstract class tracking a virtual level of {@link JSONNormalizedNodeStreamWriter}
+ * recursion. It only tracks the namespace associated with this node.
+ */
+abstract class JSONStreamWriterURIContext extends JSONStreamWriterContext {
+    private final URI namespace;
+
+    protected JSONStreamWriterURIContext(final JSONStreamWriterContext parent, final URI namespace) {
+        super(parent, false);
+        this.namespace = namespace;
+    }
+
+    @Override
+    protected final URI getNamespace() {
+        return namespace;
+    }
+
+    @Override
+    protected final void emitStart(final SchemaContext schema, final Writer writer) throws IOException {
+        // No-op
+    }
+}
\ No newline at end of file
index 66d55237a567c813d31b1d77d7e3505731ac17b8..fcbe473cca8dc6b414814a2e05ed3f92d9e3673e 100644 (file)
@@ -11,11 +11,12 @@ import com.google.common.base.Preconditions;
 
 import java.net.URI;
 
+import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringIdentityrefCodec;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
-final class JSONStringIdentityrefCodec extends AbstractModuleStringIdentityrefCodec {
+final class JSONStringIdentityrefCodec extends AbstractModuleStringIdentityrefCodec implements JSONCodec<QName> {
     private final SchemaContext context;
 
     JSONStringIdentityrefCodec(final SchemaContext context) {
@@ -33,4 +34,8 @@ final class JSONStringIdentityrefCodec extends AbstractModuleStringIdentityrefCo
         return module == null ? null : module.getName();
     }
 
+    @Override
+    public boolean needQuotes() {
+        return true;
+    }
 }
index fc1322fba9096bc3a3e906855a630e748ae71514..a1580dd5bb2c3f5681569c1c3541e68d38f51cbf 100644 (file)
@@ -11,11 +11,12 @@ import com.google.common.base.Preconditions;
 
 import java.net.URI;
 
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringInstanceIdentifierCodec;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
-final class JSONStringInstanceIdentifierCodec extends AbstractModuleStringInstanceIdentifierCodec {
+final class JSONStringInstanceIdentifierCodec extends AbstractModuleStringInstanceIdentifierCodec implements JSONCodec<YangInstanceIdentifier> {
     private final SchemaContext context;
 
     JSONStringInstanceIdentifierCodec(final SchemaContext context) {
@@ -32,4 +33,9 @@ final class JSONStringInstanceIdentifierCodec extends AbstractModuleStringInstan
         final Module module = context.findModuleByNamespaceAndRevision(namespace, null);
         return module == null ? null : module.getName();
     }
+
+    @Override
+    public boolean needQuotes() {
+        return true;
+    }
 }
index 65c234aae4b87dfef3c87fd3662cf34fd2c660d2..cea3fa683c02cee5c120370d5c680a325d1b5479 100644 (file)
@@ -50,13 +50,13 @@ import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
 public final class JsonParserStream implements Closeable, Flushable {
     private final Deque<URI> namespaces = new ArrayDeque<>();
     private final NormalizedNodeStreamWriter writer;
-    private final CodecFactory codecs;
+    private final JSONCodecFactory codecs;
     private final SchemaContext schema;
 
     private JsonParserStream(final NormalizedNodeStreamWriter writer, final SchemaContext schemaContext) {
         this.schema = Preconditions.checkNotNull(schemaContext);
         this.writer = Preconditions.checkNotNull(writer);
-        this.codecs = CodecFactory.create(schemaContext);
+        this.codecs = JSONCodecFactory.create(schemaContext);
     }
 
     public static JsonParserStream create(final NormalizedNodeStreamWriter writer, final SchemaContext schemaContext) {
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/QuotedJSONCodec.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/QuotedJSONCodec.java
new file mode 100644 (file)
index 0000000..e8606f8
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import org.opendaylight.yangtools.concepts.Codec;
+
+/**
+ * A {@link JSONCodec} which needs double quotes in output representation.
+ *
+ * @param <T> Deserialized value type
+ */
+final class QuotedJSONCodec<T> extends AbstractJSONCodec<T> {
+    QuotedJSONCodec(final Codec<String, T> codec) {
+        super(codec);
+    }
+
+    @Override
+    public boolean needQuotes() {
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/UnquotedJSONCodec.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/UnquotedJSONCodec.java
new file mode 100644 (file)
index 0000000..f29db5d
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import org.opendaylight.yangtools.concepts.Codec;
+
+/**
+ * A {@link JSONCodec} which does not need double quotes in output representation.
+ *
+ * @param <T> Deserialized value type
+ */
+final class UnquotedJSONCodec<T> extends AbstractJSONCodec<T> {
+    UnquotedJSONCodec(final Codec<String, T> codec) {
+        super(codec);
+    }
+
+    @Override
+    public boolean needQuotes() {
+        return false;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/JsonStreamToNormalizedNodeTest.java b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/JsonStreamToNormalizedNodeTest.java
new file mode 100644 (file)
index 0000000..56d1210
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import static org.junit.Assert.assertEquals;
+import static org.opendaylight.yangtools.yang.data.codec.gson.TestUtils.loadModules;
+import static org.opendaylight.yangtools.yang.data.codec.gson.TestUtils.loadTextFile;
+
+import com.google.gson.stream.JsonReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.net.URISyntaxException;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ *
+ * Each test tests whether json input is correctly transformed to normalized node structure
+ */
+public class JsonStreamToNormalizedNodeTest {
+
+    private static SchemaContext schemaContext;
+
+    @BeforeClass
+    public static void initialization() throws IOException, URISyntaxException {
+        schemaContext = loadModules("/complexjson/yang");
+    }
+
+    /**
+     * case when anyxml contains simple value will be implemented when anyxml normalized node reprezentation will be
+     * specified
+     */
+    @Ignore
+    @Test
+    public void anyXmlNodeWithSimpleValueInContainer() throws IOException, URISyntaxException {
+
+    }
+
+    /**
+     * case when anyxml contains complex xml will be implemented when anyxml normalized node reprezentation will be
+     * specified
+     */
+    @Ignore
+    @Test
+    public void anyXmlNodeWithCompositeValueInContainer() throws IOException, URISyntaxException {
+
+    }
+
+    @Test
+    public void leafNodeInContainer() throws IOException, URISyntaxException {
+        String inputJson = loadTextFile("/complexjson/leaf-node-in-container.json");
+        verifyTransformationToNormalizedNode(inputJson, TestingNormalizedNodeStructuresCreator.leafNodeInContainer());
+    }
+
+    @Test
+    public void leafNodeViaAugmentationInContainer() throws IOException, URISyntaxException {
+        String inputJson = loadTextFile("/complexjson/leaf-node-via-augmentation-in-container.json");
+        verifyTransformationToNormalizedNode(inputJson,
+                TestingNormalizedNodeStructuresCreator.leafNodeViaAugmentationInContainer());
+    }
+
+    @Test
+    public void leafListNodeInContainer() throws IOException, URISyntaxException {
+        String inputJson = loadTextFile("/complexjson/leaflist-node-in-container.json");
+        verifyTransformationToNormalizedNode(inputJson,
+                TestingNormalizedNodeStructuresCreator.leafListNodeInContainer());
+    }
+
+    @Test
+    public void keyedListNodeInContainer() throws IOException, URISyntaxException {
+        String inputJson = loadTextFile("/complexjson/keyed-list-node-in-container.json");
+        verifyTransformationToNormalizedNode(inputJson,
+                TestingNormalizedNodeStructuresCreator.keyedListNodeInContainer());
+    }
+
+    @Test
+    public void choiceNodeInContainer() throws IOException, URISyntaxException {
+        String inputJson = loadTextFile("/complexjson/choice-node-in-container.json");
+        verifyTransformationToNormalizedNode(inputJson, TestingNormalizedNodeStructuresCreator.choiceNodeInContainer());
+    }
+
+    /**
+     * Test of translating internal augmentations to normalized nodes structure
+     *
+     * 2 nodes are added via internal augmentation A, 1 node via internal augmentation B and one node is originally
+     * member of case.
+     *
+     */
+    @Test
+    public void caseNodeAugmentationInChoiceInContainer() throws IOException, URISyntaxException {
+        String inputJson = loadTextFile("/complexjson/case-node-augmentation-in-choice-in-container.json");
+        verifyTransformationToNormalizedNode(inputJson,
+                TestingNormalizedNodeStructuresCreator.caseNodeAugmentationInChoiceInContainer());
+    }
+
+    /**
+     * also test using of namesakes (equal local names with different
+     *
+     * @throws IOException
+     * @throws URISyntaxException
+     */
+    @Test
+    public void caseNodeExternalAugmentationInChoiceInContainer() throws IOException, URISyntaxException {
+        String inputJson = loadTextFile("/complexjson/case-node-external-augmentation-in-choice-in-container.json");
+        verifyTransformationToNormalizedNode(inputJson,
+                TestingNormalizedNodeStructuresCreator.caseNodeExternalAugmentationInChoiceInContainer());
+    }
+
+    /**
+     * augmentation of choice - adding new case
+     */
+    @Test
+    public void choiceNodeAugmentationInContainer() throws IOException, URISyntaxException {
+        String inputJson = loadTextFile("/complexjson/choice-node-augmentation-in-container.json");
+        verifyTransformationToNormalizedNode(inputJson,
+                TestingNormalizedNodeStructuresCreator.choiceNodeAugmentationInContainer());
+    }
+
+    @Test
+    public void unkeyedNodeInContainer() throws IOException, URISyntaxException {
+        String inputJson = loadTextFile("/complexjson/unkeyed-node-in-container.json");
+        verifyTransformationToNormalizedNode(inputJson, TestingNormalizedNodeStructuresCreator.unkeyedNodeInContainer());
+    }
+
+    private void verifyTransformationToNormalizedNode(final String inputJson,
+            final NormalizedNode<?, ?> awaitedStructure) {
+        NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        JsonParserStream jsonParser = JsonParserStream.create(streamWriter, schemaContext);
+        jsonParser.parse(new JsonReader(new StringReader(inputJson)));
+        NormalizedNode<?, ?> transformedInput = result.getResult();
+        assertEquals("Transformation of json input to normalized node wasn't successful.", awaitedStructure,
+                transformedInput);
+    }
+
+}
diff --git a/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/NormalizedNodeToJsonStreamTest.java b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/NormalizedNodeToJsonStreamTest.java
new file mode 100644 (file)
index 0000000..62e7cf1
--- /dev/null
@@ -0,0 +1,367 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.yangtools.yang.data.codec.gson.TestUtils.childArray;
+import static org.opendaylight.yangtools.yang.data.codec.gson.TestUtils.childPrimitive;
+import static org.opendaylight.yangtools.yang.data.codec.gson.TestUtils.loadModules;
+import static org.opendaylight.yangtools.yang.data.codec.gson.TestUtils.resolveCont1;
+
+import com.google.common.collect.Sets;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.net.URISyntaxException;
+import java.util.HashSet;
+import java.util.Iterator;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Each test tests whether json output obtained after transformation contains is corect. The transformation takes
+ * normalized node data structure and transform it to json output. To make it easier validate json output it is loaded
+ * via gson as structure of json elements which are walked and compared with awaited values.
+ *
+ */
+public class NormalizedNodeToJsonStreamTest {
+
+    private static SchemaContext schemaContext;
+
+    public interface JsonValidator {
+        void validate(final String jsonOutput);
+   }
+
+    @BeforeClass
+    public static void initialization() throws IOException, URISyntaxException {
+        schemaContext = loadModules("/complexjson/yang");
+    }
+
+    /**
+     * case when anyxml contains simple value will be implemented when anyxml normalized node reprezentation will be
+     * specified
+     */
+    @Ignore
+    @Test
+    public void anyXmlNodeWithSimpleValueInContainer() throws IOException, URISyntaxException {
+
+    }
+
+    /**
+     * case when anyxml contains complex xml will be implemented when anyxml normalized node reprezentation will be
+     * specified
+     */
+    @Ignore
+    @Test
+    public void anyXmlNodeWithCompositeValueInContainer() throws IOException, URISyntaxException {
+
+    }
+
+    @Test
+    public void leafNodeInContainer() throws IOException, URISyntaxException {
+        Writer writer = new StringWriter();
+        NormalizedNode<?, ?> leafNodeInContainer = TestingNormalizedNodeStructuresCreator.leafNodeInContainer();
+        String jsonOutput = normalizedNodeToJsonStreamTransformation(writer, leafNodeInContainer);
+        new JsonValidator() {
+
+            @Override
+            public void validate(String jsonOutput) {
+                JsonObject cont1 = resolveCont1(jsonOutput);
+                assertNotNull(cont1);
+
+                JsonPrimitive lf11 = childPrimitive(cont1, "complexjson:lf11", "lf11");
+                assertNotNull(lf11);
+                int asInt = lf11.getAsInt();
+                assertEquals(453, asInt);
+            }
+        }.validate(jsonOutput);
+
+    }
+
+    @Test
+    public void leafNodeViaAugmentationInContainer() throws IOException, URISyntaxException {
+        Writer writer = new StringWriter();
+        NormalizedNode<?, ?> leafNodeViaAugmentationInContainer = TestingNormalizedNodeStructuresCreator
+                .leafNodeViaAugmentationInContainer();
+        String jsonOutput = normalizedNodeToJsonStreamTransformation(writer, leafNodeViaAugmentationInContainer);
+        new JsonValidator() {
+
+            @Override
+            public void validate(String jsonOutput) {
+                JsonObject cont1 = resolveCont1(jsonOutput);
+                assertNotNull(cont1);
+
+                JsonPrimitive lf12_1 = childPrimitive(cont1, "complexjson:lf12_1", "lf12_1");
+                assertNotNull(lf12_1);
+                String asString = lf12_1.getAsString();
+                assertEquals("lf12 value", asString);
+            }
+        }.validate(jsonOutput);
+
+    }
+
+    @Test
+    public void leafListNodeInContainer() throws IOException, URISyntaxException {
+        Writer writer = new StringWriter();
+        NormalizedNode<?, ?> leafListNodeInContainer = TestingNormalizedNodeStructuresCreator.leafListNodeInContainer();
+        String jsonOutput = normalizedNodeToJsonStreamTransformation(writer, leafListNodeInContainer);
+        new JsonValidator() {
+
+            @Override
+            public void validate(String jsonOutput) {
+                JsonObject cont1 = resolveCont1(jsonOutput);
+                assertNotNull(cont1);
+                JsonArray lflst11 = childArray(cont1, "complexjson:lflst11", "lflst11");
+                assertNotNull(lflst11);
+
+                HashSet<Object> lflst11Values = Sets.newHashSet();
+                for (JsonElement jsonElement : lflst11) {
+                    assertTrue(jsonElement instanceof JsonPrimitive);
+                    lflst11Values.add(((JsonPrimitive) jsonElement).getAsString());
+                }
+
+                assertEquals(Sets.newHashSet("lflst11 value2", "lflst11 value1"), lflst11Values);
+            }
+        }.validate(jsonOutput);
+    }
+
+    @Test
+    public void keyedListNodeInContainer() throws IOException, URISyntaxException {
+        Writer writer = new StringWriter();
+        NormalizedNode<?, ?> keyedListNodeInContainer = TestingNormalizedNodeStructuresCreator
+                .keyedListNodeInContainer();
+        String jsonOutput = normalizedNodeToJsonStreamTransformation(writer, keyedListNodeInContainer);
+        new JsonValidator() {
+
+            @Override
+            public void validate(String jsonOutput) {
+                JsonObject cont1 = resolveCont1(jsonOutput);
+                assertNotNull(cont1);
+                JsonArray lst11 = childArray(cont1, "complexjson:lst11", "lst11");
+                assertNotNull(lst11);
+
+                Iterator<JsonElement> iterator = lst11.iterator();
+                assertTrue(iterator.hasNext());
+                JsonElement lst11Entry1Raw = iterator.next();
+                assertFalse(iterator.hasNext());
+                assertTrue(lst11Entry1Raw instanceof JsonObject);
+                JsonObject lst11Entry1 = (JsonObject) lst11Entry1Raw;
+
+                JsonPrimitive key111 = childPrimitive(lst11Entry1, "complexjson:key111", "key111");
+                assertNotNull(key111);
+                JsonPrimitive lf112 = childPrimitive(lst11Entry1, "complexjson:lf112", "lf112");
+                assertNotNull(lf112);
+                JsonPrimitive lf113 = childPrimitive(lst11Entry1, "complexjson:lf113", "lf113");
+                assertNotNull(lf113);
+                JsonPrimitive lf111 = childPrimitive(lst11Entry1, "complexjson:lf111", "lf111");
+                assertNotNull(lf111);
+
+                assertEquals("key111 value", key111.getAsString());
+                assertEquals("/complexjson:cont1/complexjson:lflst11", lf112.getAsString());
+                assertEquals("lf113 value", lf113.getAsString());
+                assertEquals("lf111 value", lf111.getAsString());
+            }
+        }.validate(jsonOutput);
+    }
+
+    @Test
+    public void choiceNodeInContainer() throws IOException, URISyntaxException {
+        Writer writer = new StringWriter();
+        NormalizedNode<?, ?> choiceNodeInContainer = TestingNormalizedNodeStructuresCreator.choiceNodeInContainer();
+        String jsonOutput = normalizedNodeToJsonStreamTransformation(writer, choiceNodeInContainer);
+        new JsonValidator() {
+
+            @Override
+            public void validate(String jsonOutput) {
+                JsonObject cont1 = resolveCont1(jsonOutput);
+                assertNotNull(cont1);
+                JsonPrimitive lf13 = childPrimitive(cont1, "complexjson:lf13", "lf13");
+                assertNotNull(lf13);
+
+                assertEquals("lf13 value", lf13.getAsString());
+            }
+        }.validate(jsonOutput);
+    }
+
+    /**
+     * tested case when case c11A in choice choc11 is augmented (two leaves (augment A) and one leaf (augment B) are
+     * added)
+     *
+     * after running this test following exception is raised
+     *
+     * java.lang.IllegalArgumentException: Augmentation allowed only in DataNodeContainer
+     * [ChoiceNodeImpl[qname=(ns:complex:json?revision=2014-08-11)choc11]]
+     *
+     */
+//    @Ignore
+    @Test
+    public void caseNodeAugmentationInChoiceInContainer() throws IOException, URISyntaxException {
+        Writer writer = new StringWriter();
+        NormalizedNode<?, ?> caseNodeAugmentationInChoiceInContainer = TestingNormalizedNodeStructuresCreator
+                .caseNodeAugmentationInChoiceInContainer();
+        String jsonOutput = normalizedNodeToJsonStreamTransformation(writer, caseNodeAugmentationInChoiceInContainer);
+        new JsonValidator() {
+
+            @Override
+            public void validate(String jsonOutput) {
+                JsonObject cont1 = resolveCont1(jsonOutput);
+                assertNotNull(cont1);
+
+                JsonPrimitive lf15_21 = childPrimitive(cont1, "complexjson:lf15_21", "lf15_21");
+                assertNotNull(lf15_21);
+                JsonPrimitive lf13 = childPrimitive(cont1, "complexjson:lf13", "lf13");
+                assertNotNull(lf13);
+                JsonPrimitive lf15_11 = childPrimitive(cont1, "complexjson:lf15_11", "lf15_11");
+                assertNotNull(lf15_11);
+                JsonPrimitive lf15_12 = childPrimitive(cont1, "complexjson:lf15_12", "lf15_12");
+                assertNotNull(lf15_12);
+
+                assertEquals("lf15_21 value", lf15_21.getAsString());
+                assertEquals("lf13 value", lf13.getAsString());
+                assertTrue("one two".equals(lf15_11.getAsString()) || "two one".equals(lf15_11.getAsString()));
+                assertEquals("complexjson:lf11", lf15_12.getAsString());
+
+            }
+        }.validate(jsonOutput);
+    }
+
+    /**
+     * tested case when case c11A in choice choc11 is augmented (two leaves (augment A) internally and one two leaves
+     * with the same names externally (augment B) are added)
+     *
+     * after running this test following exception is raised
+     *
+     * java.lang.IllegalArgumentException: Augmentation allowed only in DataNodeContainer
+     * [ChoiceNodeImpl[qname=(ns:complex:json?revision=2014-08-11)choc11]]
+     *
+     */
+//    @Ignore
+    @Test
+    public void caseNodeExternalAugmentationInChoiceInContainer() throws IOException, URISyntaxException {
+        Writer writer = new StringWriter();
+        NormalizedNode<?, ?> caseNodeExternalAugmentationInChoiceInContainer = TestingNormalizedNodeStructuresCreator
+                .caseNodeExternalAugmentationInChoiceInContainer();
+        String jsonOutput = normalizedNodeToJsonStreamTransformation(writer,
+                caseNodeExternalAugmentationInChoiceInContainer);
+        new JsonValidator() {
+
+            @Override
+            public void validate(String jsonOutput) {
+                JsonObject cont1 = resolveCont1(jsonOutput);
+                assertNotNull(cont1);
+
+                JsonPrimitive lf15_11Augment = childPrimitive(cont1, "complexjson-augmentation:lf15_11");
+                assertNotNull(lf15_11Augment);
+                JsonPrimitive lf15_12Augment = childPrimitive(cont1, "complexjson-augmentation:lf15_12");
+                assertNotNull(lf15_12Augment);
+                JsonPrimitive lf13 = childPrimitive(cont1, "complexjson:lf13", "lf13");
+                assertNotNull(lf13);
+                JsonPrimitive lf15_11 = childPrimitive(cont1, "complexjson:lf15_11", "lf15_11");
+                assertNotNull(lf15_11);
+                JsonPrimitive lf15_12 = childPrimitive(cont1, "complexjson:lf15_12", "lf15_12");
+                assertNotNull(lf15_12);
+
+                assertEquals("lf15_11 value from augmentation", lf15_11Augment.getAsString());
+                assertEquals("lf15_12 value from augmentation", lf15_12Augment.getAsString());
+                assertEquals("lf13 value", lf13.getAsString());
+                assertTrue("one two".equals(lf15_11.getAsString()) || "two one".equals(lf15_11.getAsString()));
+                assertEquals("complexjson:lf11", lf15_12.getAsString());
+
+            }
+        }.validate(jsonOutput);
+    }
+
+    /**
+     * augmentation of choice - adding new case
+     *
+     * after running this test following exception is raised
+     *
+     * java.lang.IllegalArgumentException: Augmentation allowed only in DataNodeContainer
+     * [ChoiceNodeImpl[qname=(ns:complex:json?revision=2014-08-11)choc11]]
+     *
+     */
+//    @Ignore
+    @Test
+    public void choiceNodeAugmentationInContainer() throws IOException, URISyntaxException {
+        Writer writer = new StringWriter();
+        NormalizedNode<?, ?> choiceNodeAugmentationInContainer = TestingNormalizedNodeStructuresCreator
+                .choiceNodeAugmentationInContainer();
+        String jsonOutput = normalizedNodeToJsonStreamTransformation(writer,
+                choiceNodeAugmentationInContainer);
+        new JsonValidator() {
+
+            @Override
+            public void validate(String jsonOutput) {
+                JsonObject cont1 = resolveCont1(jsonOutput);
+                assertNotNull(cont1);
+
+                JsonPrimitive lf17 = childPrimitive(cont1, "complexjson:lf17","lf17");
+                assertNotNull(lf17);
+                assertEquals("lf17 value",lf17.getAsString());
+            }
+        }.validate(jsonOutput);
+    }
+
+    @Test
+    public void unkeyedNodeInContainer() throws IOException, URISyntaxException {
+        Writer writer = new StringWriter();
+        NormalizedNode<?, ?> unkeyedNodeInContainer = TestingNormalizedNodeStructuresCreator
+                .unkeyedNodeInContainer();
+        String jsonOutput = normalizedNodeToJsonStreamTransformation(writer,
+                unkeyedNodeInContainer);
+        new JsonValidator() {
+
+            @Override
+            public void validate(String jsonOutput) {
+                JsonObject cont1 = resolveCont1(jsonOutput);
+                assertNotNull(cont1);
+
+                JsonArray lst12 = childArray(cont1, "complexjson:lst12","lst12");
+                assertNotNull(lst12);
+
+                Iterator<JsonElement> iterator = lst12.iterator();
+                assertTrue(iterator.hasNext());
+                JsonElement lst12Entry1Raw = iterator.next();
+                assertFalse(iterator.hasNext());
+
+                assertTrue(lst12Entry1Raw instanceof JsonObject);
+                JsonObject lst12Entry1 = (JsonObject)lst12Entry1Raw;
+                JsonPrimitive lf121 = childPrimitive(lst12Entry1, "complexjson:lf121", "lf121");
+                assertNotNull(lf121);
+
+                assertEquals("lf121 value",lf121.getAsString());
+
+            }
+        }.validate(jsonOutput);
+
+    }
+
+    private String normalizedNodeToJsonStreamTransformation(final Writer writer,
+            final NormalizedNode<?, ?> inputStructure) throws IOException {
+        writer.write("{\n");
+        final NormalizedNodeStreamWriter jsonStream = JSONNormalizedNodeStreamWriter.create(schemaContext, writer, 2);
+        final NormalizedNodeWriter nodeWriter = NormalizedNodeWriter.forStreamWriter(jsonStream);
+        nodeWriter.write(inputStructure);
+        writer.write("\n}");
+        nodeWriter.close();
+        return writer.toString();
+    }
+
+}
index 534b3bdb445759685121c033e4b1a2b15375b326..f32caaa12d6f3269d3ea0be318b069059cc69f6d 100644 (file)
@@ -7,25 +7,19 @@
  */
 package org.opendaylight.yangtools.yang.data.codec.gson;
 
-import com.google.gson.stream.JsonReader;
+import static org.opendaylight.yangtools.yang.data.codec.gson.TestUtils.loadModules;
+import static org.opendaylight.yangtools.yang.data.codec.gson.TestUtils.loadTextFile;
 
-import java.io.BufferedReader;
+import com.google.gson.stream.JsonReader;
 import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
 import java.io.IOException;
 import java.io.StringReader;
 import java.io.StringWriter;
-import java.net.URI;
 import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.List;
-
 import org.junit.BeforeClass;
 import org.junit.Ignore;
 import org.junit.Test;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
-import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.api.schema.stream.LoggingNormalizedNodeStreamWriter;
@@ -34,8 +28,6 @@ import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWrit
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
 import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.parser.api.YangContextParser;
-import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -121,33 +113,4 @@ public class StreamToNormalizedNodeTest {
         LOG.debug("Serialized JSON: {}", writer.toString());
     }
 
-    private static SchemaContext loadModules(final String resourceDirectory) throws IOException, URISyntaxException {
-        YangContextParser parser = new YangParserImpl();
-        URI path = StreamToNormalizedNodeTest.class.getResource(resourceDirectory).toURI();
-        final File testDir = new File(path);
-        final String[] fileList = testDir.list();
-        final List<File> testFiles = new ArrayList<File>();
-        if (fileList == null) {
-            throw new FileNotFoundException(resourceDirectory);
-        }
-        for (String fileName : fileList) {
-            if (new File(testDir, fileName).isDirectory() == false) {
-                testFiles.add(new File(testDir, fileName));
-            }
-        }
-        return parser.parseFiles(testFiles);
-    }
-
-    private static String loadTextFile(final File file) throws IOException {
-        FileReader fileReader = new FileReader(file);
-        BufferedReader bufReader = new BufferedReader(fileReader);
-
-        String line = null;
-        StringBuilder result = new StringBuilder();
-        while ((line = bufReader.readLine()) != null) {
-            result.append(line);
-        }
-        bufReader.close();
-        return result.toString();
-    }
 }
diff --git a/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/TestUtils.java b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/TestUtils.java
new file mode 100644 (file)
index 0000000..d83e159
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import static org.junit.Assert.assertTrue;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.google.gson.JsonPrimitive;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.parser.api.YangContextParser;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+
+public class TestUtils {
+
+    private TestUtils() {
+    }
+
+    static SchemaContext loadModules(final String resourceDirectory) throws IOException, URISyntaxException {
+        YangContextParser parser = new YangParserImpl();
+        URI path = StreamToNormalizedNodeTest.class.getResource(resourceDirectory).toURI();
+        final File testDir = new File(path);
+        final String[] fileList = testDir.list();
+        final List<File> testFiles = new ArrayList<File>();
+        if (fileList == null) {
+            throw new FileNotFoundException(resourceDirectory);
+        }
+        for (String fileName : fileList) {
+            if (new File(testDir, fileName).isDirectory() == false) {
+                testFiles.add(new File(testDir, fileName));
+            }
+        }
+        return parser.parseFiles(testFiles);
+    }
+
+    static String loadTextFile(final File file) throws IOException {
+        FileReader fileReader = new FileReader(file);
+        BufferedReader bufReader = new BufferedReader(fileReader);
+
+        String line = null;
+        StringBuilder result = new StringBuilder();
+        while ((line = bufReader.readLine()) != null) {
+            result.append(line);
+        }
+        bufReader.close();
+        return result.toString();
+    }
+
+    static String loadTextFile(final String relativePath) throws IOException, URISyntaxException {
+        return loadTextFile(new File(TestUtils.class.getResource(relativePath).toURI()));
+    }
+
+    static JsonObject childObject(final JsonObject jsonObject,final String ... names) {
+        for (String name : names) {
+            JsonObject childJsonObject = jsonObject.getAsJsonObject(name);
+            if (childJsonObject != null) {
+                return childJsonObject;
+            }
+        }
+        return null;
+    }
+
+    static JsonPrimitive childPrimitive(final JsonObject jsonObject,final String ... names) {
+        for (String name : names) {
+            JsonPrimitive childJsonPrimitive = jsonObject.getAsJsonPrimitive(name);
+            if (childJsonPrimitive != null) {
+                return childJsonPrimitive;
+            }
+        }
+        return null;
+    }
+
+    static JsonArray childArray(final JsonObject jsonObject,final String ... names) {
+        for (String name : names) {
+            JsonArray childJsonArray = jsonObject.getAsJsonArray(name);
+            if (childJsonArray != null) {
+                return childJsonArray;
+            }
+        }
+        return null;
+    }
+
+    static JsonObject resolveCont1(String jsonOutput) {
+        JsonParser parser = new JsonParser();
+        JsonElement rootElement = parser.parse(jsonOutput);
+        assertTrue(rootElement.isJsonObject());
+        JsonObject rootObject = rootElement.getAsJsonObject();
+        JsonObject cont1 = childObject(rootObject, "complexjson:cont1", "cont1");
+        return cont1;
+    }
+
+
+}
diff --git a/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/TestingNormalizedNodeStructuresCreator.java b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/TestingNormalizedNodeStructuresCreator.java
new file mode 100644 (file)
index 0000000..f140085
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import java.util.HashMap;
+import java.util.Map;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.SimpleNode;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
+
+public class TestingNormalizedNodeStructuresCreator {
+
+    static NormalizedNode<?, ?> cont1Node(
+            DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?>... children) {
+        DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> cont1 = Builders.containerBuilder();
+        cont1.withNodeIdentifier(new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "cont1")));
+
+        cont1.withValue(Lists.newArrayList(children));
+        return cont1.build();
+    }
+
+    private static DataContainerChild<? extends PathArgument, ?> lst12Node() {
+        CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> lst12Builder = Builders.unkeyedListBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "lst12")));
+        lst12Builder.withChild(lst12Entry1Node());
+        return lst12Builder.build();
+    }
+
+    private static UnkeyedListEntryNode lst12Entry1Node() {
+        DataContainerNodeAttrBuilder<NodeIdentifier, UnkeyedListEntryNode> lst12Entry1Builder = Builders
+                .unkeyedListEntryBuilder();
+        lst12Entry1Builder
+                .withNodeIdentifier(new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "lst12")));
+        lst12Entry1Builder.withChild(Builders.leafBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "lf121")))
+                .withValue("lf121 value").build());
+        return lst12Entry1Builder.build();
+    }
+
+    private static DataContainerChild<? extends PathArgument, ?> choc12Node() {
+        DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> choc12Builder = Builders.choiceBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "choc12")));
+
+//        DataContainerNodeBuilder<AugmentationIdentifier, AugmentationNode> choc12Augmentation = Builders
+//                .augmentationBuilder();
+//        choc12Augmentation.withNodeIdentifier(new AugmentationIdentifier(Sets.newHashSet(QName.create(
+//                "ns:complex:json", "2014-08-11", "c12B"))));
+//        choc12Augmentation.withChild(lf17Node());
+
+        choc12Builder.withChild(lf17Node());
+        return choc12Builder.build();
+    }
+
+    protected static LeafNode<Object> lf17Node() {
+        return Builders.leafBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "lf17")))
+                .withValue("lf17 value").build();
+    }
+
+    private static DataContainerChild<? extends PathArgument, ?> externalAugmentC11AWithLf15_11AndLf15_12Node() {
+        DataContainerNodeBuilder<AugmentationIdentifier, AugmentationNode> augmentationBuilder = Builders
+                .augmentationBuilder();
+        augmentationBuilder.withNodeIdentifier(new AugmentationIdentifier(Sets.newHashSet(
+                QName.create("ns:complex:json:augmentation", "2014-08-14", "lf15_11"),
+                QName.create("ns:complex:json:augmentation", "2014-08-14", "lf15_12"))));
+        augmentationBuilder.withChild(lf15_11NodeExternal());
+        augmentationBuilder.withChild(lf15_12NodeExternal());
+        return augmentationBuilder.build();
+    }
+
+    private static LeafNode<Object> lf15_12NodeExternal() {
+        return Builders
+                .leafBuilder()
+                .withNodeIdentifier(
+                        new NodeIdentifier(QName.create("ns:complex:json:augmentation", "2014-08-14", "lf15_12")))
+                .withValue("lf15_12 value from augmentation").build();
+    }
+
+    private static LeafNode<Object> lf15_11NodeExternal() {
+        return Builders
+                .leafBuilder()
+                .withNodeIdentifier(
+                        new NodeIdentifier(QName.create("ns:complex:json:augmentation", "2014-08-14", "lf15_11")))
+                .withValue("lf15_11 value from augmentation").build();
+    }
+
+    private static DataContainerChild<? extends PathArgument, ?> choc11Node(
+            DataContainerChild<? extends PathArgument, ?>... children) {
+        DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> choc11Builder = Builders.choiceBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "choc11")));
+        choc11Builder.withValue(Lists.newArrayList(children));
+        // choc11Builder.addChild(lf13Node());
+        // choc11Builder.addChild(augmentChoc11_c11A_lf1511AndLf1512Children());
+        // choc11Builder.addChild(augmentChoc11_c11_lf1521Children());
+        return choc11Builder.build();
+    }
+
+    private static LeafNode<Object> lf13Node() {
+        return Builders.leafBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "lf13")))
+                .withValue("lf13 value").build();
+    }
+
+    private static DataContainerChild<? extends PathArgument, ?> augmentC11AWithLf15_21Node() {
+        DataContainerNodeBuilder<AugmentationIdentifier, AugmentationNode> choc11_c11AugmentBuilder = Builders
+                .augmentationBuilder();
+        choc11_c11AugmentBuilder.withNodeIdentifier(new AugmentationIdentifier(Sets.newHashSet(QName.create(
+                "ns:complex:json", "2014-08-11", "lf15_21"))));
+
+        choc11_c11AugmentBuilder.withChild(lf15_21Node());
+        return choc11_c11AugmentBuilder.build();
+    }
+
+    private static LeafNode<Object> lf15_21Node() {
+        return Builders.leafBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "lf15_21")))
+                .withValue("lf15_21 value").build();
+    }
+
+    private static DataContainerChild<? extends PathArgument, ?> augmentC11AWithLf15_11AndLf15_12Node() {
+        DataContainerNodeBuilder<AugmentationIdentifier, AugmentationNode> choc11_c11AugmentBuilder = Builders
+                .augmentationBuilder();
+        choc11_c11AugmentBuilder.withNodeIdentifier(new AugmentationIdentifier(Sets.newHashSet(
+                QName.create("ns:complex:json", "2014-08-11", "lf15_11"),
+                QName.create("ns:complex:json", "2014-08-11", "lf15_12"))));
+        choc11_c11AugmentBuilder.withChild(lf15_11Node());
+        choc11_c11AugmentBuilder.withChild(lf15_12Node());
+        return choc11_c11AugmentBuilder.build();
+    }
+
+    private static LeafNode<Object> lf15_12Node() {
+        return Builders.leafBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "lf15_12")))
+                .withValue(QName.create("ns:complex:json", "2014-08-11", "lf11")).build();
+    }
+
+    private static LeafNode<Object> lf15_11Node() {
+        return Builders.leafBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "lf15_11")))
+                .withValue(Sets.newHashSet("one", "two")).build();
+    }
+
+    private static DataContainerChild<? extends PathArgument, ?> lf12_1Node() {
+        DataContainerNodeBuilder<AugmentationIdentifier, AugmentationNode> augmentBuilder = Builders
+                .augmentationBuilder().withNodeIdentifier(
+                        new AugmentationIdentifier(Sets.newHashSet(
+                                QName.create("ns:complex:json", "2014-08-11", "lf12_1"),
+                                QName.create("ns:complex:json", "2014-08-11", "lf12_2"))));
+        augmentBuilder.withChild(Builders.leafBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "lf12_1")))
+                .withValue("lf12 value").build());
+        return augmentBuilder.build();
+    }
+
+    private static DataContainerChild<? extends PathArgument, ?> childLst11() {
+        CollectionNodeBuilder<MapEntryNode, MapNode> lst11 = Builders.mapBuilder().withNodeIdentifier(
+                new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "lst11")));
+
+        DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> lst11Entry1Builder = Builders
+                .mapEntryBuilder();
+
+        Map<QName, Object> key = new HashMap<>();
+        key.put(QName.create("ns:complex:json", "2014-08-11", "key111"), "key111 value");
+        key.put(QName.create("ns:complex:json", "2014-08-11", "lf111"), "lf111 value");
+
+        lst11Entry1Builder.withNodeIdentifier(new NodeIdentifierWithPredicates(QName.create("ns:complex:json",
+                "2014-08-11", "lst11"), key));
+        lst11Entry1Builder.withChild(Builders.leafBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "key111")))
+                .withValue("key111 value").build());
+        lst11Entry1Builder.withChild(Builders.leafBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "lf112")))
+                .withValue(lf112Value()).build());
+        lst11Entry1Builder.withChild(Builders.leafBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "lf113")))
+                .withValue("lf113 value").build());
+        lst11Entry1Builder.withChild(Builders.leafBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "lf111")))
+                .withValue("lf111 value").build());
+        lst11.withChild(lst11Entry1Builder.build());
+        return lst11.build();
+    }
+
+    private static Object lf112Value() {
+        InstanceIdentifierBuilder builder = YangInstanceIdentifier.builder();
+        builder.node(QName.create("ns:complex:json", "2014-08-11", "cont1"));
+        builder.node(QName.create("ns:complex:json", "2014-08-11", "lflst11"));
+        return builder.build();
+    }
+
+    private static DataContainerChild<? extends PathArgument, ?> childLflst11() {
+        ListNodeBuilder<Object, LeafSetEntryNode<Object>> lflst11 = Builders.leafSetBuilder().withNodeIdentifier(
+                new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "lflst11")));
+        lflst11.withChild(Builders
+                .leafSetEntryBuilder()
+                .withNodeIdentifier(
+                        new NodeWithValue(QName.create("ns:complex:json", "2014-08-11", "lflst11"), "lflst11 value1"))
+                .withValue("lflst11 value1").build());
+        lflst11.withChild(Builders
+                .leafSetEntryBuilder()
+                .withNodeIdentifier(
+                        new NodeWithValue(QName.create("ns:complex:json", "2014-08-11", "lflst11"), "lflst11 value2"))
+                .withValue("lflst11 value2").build());
+        return lflst11.build();
+    }
+
+    private static CompositeNode prepareLf12Value() {
+        SimpleNode<?> anyxmlInData = NodeFactory.createImmutableSimpleNode(
+                QName.create("ns:complex:json", "2014-08-11", "anyxml-in-data"), null, "foo");
+        return ImmutableCompositeNode.builder().add(anyxmlInData)
+                .setQName(QName.create("ns:complex:json", "2014-08-11", "lf12-any")).toInstance();
+    }
+
+    private static CompositeNode prepareLf13Value() {
+        SimpleNode<?> anyxmlInData = NodeFactory.createImmutableSimpleNode(
+                QName.create("ns:complex:json", "2014-08-11", "anyxml-in-data"), null, "foo");
+        return ImmutableCompositeNode.builder().add(anyxmlInData)
+                .setQName(QName.create("ns:complex:json", "2014-08-11", "lf13-any")).toInstance();
+    }
+
+    public static NormalizedNode<?, ?> leafNodeInContainer() {
+        LeafNode<Object> lf11 = Builders.leafBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "lf11")))
+                .withValue((int) 453).build();
+        return cont1Node(lf11);
+    }
+
+    public static NormalizedNode<?, ?> leafListNodeInContainer() {
+        return cont1Node(childLflst11());
+    }
+
+    public static NormalizedNode<?, ?> keyedListNodeInContainer() {
+        return cont1Node(childLst11());
+    }
+
+    public static NormalizedNode<?, ?> leafNodeViaAugmentationInContainer() {
+        return cont1Node(lf12_1Node());
+    }
+
+    public static NormalizedNode<?, ?> choiceNodeInContainer() {
+        return cont1Node(choc11Node(lf13Node()));
+    }
+
+    /**
+     * choc11 contains lf13, lf15_11 and lf15_12 are added via external augmentation
+     *
+     * @return
+     */
+    public static NormalizedNode<?, ?> caseNodeAugmentationInChoiceInContainer() {
+        return cont1Node(choc11Node(lf13Node(), lf15_11Node(), lf15_12Node(), lf15_21Node()));
+    }
+
+    public static NormalizedNode<?, ?> caseNodeExternalAugmentationInChoiceInContainer() {
+        return cont1Node(choc11Node(lf13Node(), lf15_11Node(), lf15_12Node(), lf15_11NodeExternal(), lf15_12NodeExternal()));
+    }
+
+    public static NormalizedNode<?, ?> choiceNodeAugmentationInContainer() {
+        return cont1Node(choc12Node());
+    }
+
+    public static NormalizedNode<?, ?> unkeyedNodeInContainer() {
+        return cont1Node(lst12Node());
+    }
+
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/case-node-augmentation-in-choice-in-container.json b/yang/yang-data-codec-gson/src/test/resources/complexjson/case-node-augmentation-in-choice-in-container.json
new file mode 100644 (file)
index 0000000..8b771bc
--- /dev/null
@@ -0,0 +1,8 @@
+{
+    "complexjson:cont1": {
+        "lf15_11" : "one two",        
+        "lf13" : "lf13 value",        
+        "lf15_21" : "lf15_21 value",
+        "lf15_12" : "complexjson:lf11"
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/case-node-external-augmentation-in-choice-in-container.json b/yang/yang-data-codec-gson/src/test/resources/complexjson/case-node-external-augmentation-in-choice-in-container.json
new file mode 100644 (file)
index 0000000..b15dce8
--- /dev/null
@@ -0,0 +1,9 @@
+{
+    "complexjson:cont1": {
+        "complexjson-augmentation:lf15_11" : "lf15_11 value from augmentation",
+        "lf15_11" : "one two",        
+        "lf13" : "lf13 value",        
+        "complexjson-augmentation:lf15_12" : "lf15_12 value from augmentation",
+        "lf15_12" : "complexjson:lf11"
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/choice-node-augmentation-in-container.json b/yang/yang-data-codec-gson/src/test/resources/complexjson/choice-node-augmentation-in-container.json
new file mode 100644 (file)
index 0000000..ccf7a71
--- /dev/null
@@ -0,0 +1,5 @@
+{
+    "complexjson:cont1": {
+        "lf17" : "lf17 value"
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/choice-node-in-container.json b/yang/yang-data-codec-gson/src/test/resources/complexjson/choice-node-in-container.json
new file mode 100644 (file)
index 0000000..fbbd2a8
--- /dev/null
@@ -0,0 +1,5 @@
+{
+    "complexjson:cont1": {
+        "lf13" : "lf13 value"
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/keyed-list-node-in-container.json b/yang/yang-data-codec-gson/src/test/resources/complexjson/keyed-list-node-in-container.json
new file mode 100644 (file)
index 0000000..7cb5193
--- /dev/null
@@ -0,0 +1,12 @@
+{
+    "complexjson:cont1": {
+        "lst11":[
+            {
+                "key111":"key111 value",
+                "lf112":"/complexjson:cont1/complexjson:lflst11",
+                "lf113":"lf113 value",
+                "lf111":"lf111 value"
+            }
+        ]
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/leaf-node-in-container.json b/yang/yang-data-codec-gson/src/test/resources/complexjson/leaf-node-in-container.json
new file mode 100644 (file)
index 0000000..74ad7d9
--- /dev/null
@@ -0,0 +1,5 @@
+{
+    "complexjson:cont1": {
+        "lf11" : "453"
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/leaf-node-via-augmentation-in-container.json b/yang/yang-data-codec-gson/src/test/resources/complexjson/leaf-node-via-augmentation-in-container.json
new file mode 100644 (file)
index 0000000..4995124
--- /dev/null
@@ -0,0 +1,5 @@
+{
+    "complexjson:cont1": {
+        "lf12_1" : "lf12 value"
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/leaflist-node-in-container.json b/yang/yang-data-codec-gson/src/test/resources/complexjson/leaflist-node-in-container.json
new file mode 100644 (file)
index 0000000..8941a4e
--- /dev/null
@@ -0,0 +1,5 @@
+{
+    "complexjson:cont1": {
+        "lflst11":["lflst11 value1","lflst11 value2"]
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/unkeyed-node-in-container.json b/yang/yang-data-codec-gson/src/test/resources/complexjson/unkeyed-node-in-container.json
new file mode 100644 (file)
index 0000000..8b3e1e3
--- /dev/null
@@ -0,0 +1,9 @@
+{
+    "complexjson:cont1": {
+        "lst12":[
+            {
+                "lf121":"lf121 value"
+            }
+        ]
+    }
+}
index cd43988642ea28a89e688afea014c298fba41928..01bfe32b2811d9afaf93040f6c8c112af899bdf7 100644 (file)
@@ -16,18 +16,41 @@ import org.opendaylight.yangtools.concepts.Immutable;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
-public abstract class AbstractImmutableDataContainerNode<K extends PathArgument> extends AbstractImmutableNormalizedNode<K, Iterable<DataContainerChild<? extends PathArgument, ?>>>
-implements Immutable, DataContainerNode<K> {
-
-    protected final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> children;
-    private final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> publicChildren;
+public abstract class AbstractImmutableDataContainerNode<K extends PathArgument> extends AbstractImmutableNormalizedNode<K, Iterable<DataContainerChild<? extends PathArgument, ?>>> implements Immutable, DataContainerNode<K> {
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractImmutableDataContainerNode.class);
+    private final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> children;
 
     public AbstractImmutableDataContainerNode(
             final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> children, final K nodeIdentifier) {
         super(nodeIdentifier);
-        this.children = children;
-        this.publicChildren = Collections.unmodifiableMap(children);
+
+        /*
+         * There is a code path where AbstractImmutableDataContainerNodeBuilder can reflect
+         * the collection acquired via getChildren() back to us. This is typically the case
+         * in the datastore where transactions cancel each other out, leaving an unmodified
+         * node. In that case we want to skip wrapping the map again (and again and again).
+         *
+         * In a perfect world, Collection.unmodifiableMap() would be doing the instanceof
+         * check which would stop the proliferation. Unfortunately this not the case and the
+         * 'unmodifiable' trait is not exposed by anything we can query. Furthermore the API
+         * contract there is sufficiently vague so an implementation may actually return a
+         * different implementation based on input map -- for example
+         * Collections.unmodifiableMap(Collections.emptyMap()) returning the same thing as
+         * Collections.emptyMap().
+         *
+         * This means that we have to perform the instantiation here (as opposed to once at
+         * class load time) and then compare the classes.
+         */
+        final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> pub = Collections.unmodifiableMap(children);
+        if (children.getClass().equals(pub.getClass())) {
+            LOG.trace("Reusing already-unmodifiable children {}", children);
+            this.children = children;
+        } else {
+            this.children = pub;
+        }
     }
 
     @Override
@@ -37,7 +60,7 @@ implements Immutable, DataContainerNode<K> {
 
     @Override
     public final Iterable<DataContainerChild<? extends PathArgument, ?>> getValue() {
-        return publicChildren.values();
+        return children.values();
     }
 
     @Override
@@ -46,7 +69,7 @@ implements Immutable, DataContainerNode<K> {
     }
 
     public final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> getChildren() {
-        return publicChildren;
+        return children;
     }
 
     @Override
index 4ad092e582d0196c434625a7a2dc3493bcc3642b..a993efa6cf4437bf3fa5ee71a305ede41d95ccf3 100644 (file)
     <description>${project.artifactId}</description>
 
     <dependencies>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>object-cache-api</artifactId>
+        </dependency>
         <dependency>
             <groupId>${project.groupId}</groupId>
             <artifactId>util</artifactId>
index 4e1fd00f20750388fb4af2579bc7af8f65ef9916..44a8cceee7609533e079e4aa4c64fa318469918e 100644 (file)
@@ -13,6 +13,8 @@ import com.google.common.base.Preconditions;
 
 import org.opendaylight.yangtools.concepts.Identifier;
 import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.objcache.ObjectCache;
+import org.opendaylight.yangtools.objcache.ObjectCacheFactory;
 
 /**
  * YANG Schema source identifier
@@ -37,26 +39,24 @@ import org.opendaylight.yangtools.concepts.Immutable;
  */
 @Beta
 public final class SourceIdentifier implements Identifier, Immutable {
-
     /**
      * Default revision for sources without specified revision.
      * Marks the source as oldest.
      */
     public static final String NOT_PRESENT_FORMATTED_REVISION = "0000-00-00";
 
+    private static final ObjectCache CACHE = ObjectCacheFactory.getObjectCache(SourceIdentifier.class);
     private static final long serialVersionUID = 1L;
     private final String revision;
     private final String name;
 
     /**
-     *
      * Creates new YANG Schema source identifier.
      *
      * @param name Name of schema
      * @param formattedRevision Revision of source in format YYYY-mm-dd
      */
     public SourceIdentifier(final String name, final String formattedRevision) {
-        super();
         this.name = Preconditions.checkNotNull(name);
         this.revision = Preconditions.checkNotNull(formattedRevision);
     }
@@ -72,6 +72,15 @@ public final class SourceIdentifier implements Identifier, Immutable {
         this(name, formattedRevision.or(NOT_PRESENT_FORMATTED_REVISION));
     }
 
+    /**
+     * Return a cached reference to an object equal to this object.
+     *
+     * @return A potentially shared reference, not guaranteed to be unique.
+     */
+    public SourceIdentifier cachedReference() {
+        return CACHE.getReference(this);
+    }
+
     /**
      *
      * Creates new YANG Schema source identifier for sources without revision.
@@ -83,7 +92,6 @@ public final class SourceIdentifier implements Identifier, Immutable {
         this(name, NOT_PRESENT_FORMATTED_REVISION);
     }
 
-
     /**
      * Returns model name
      *
index fae44679e2a750b413980003dddaa93b94d5cc25..fa2758f4e3ab5679ef47dca107170a3ab761032d 100644 (file)
@@ -10,6 +10,8 @@ package org.opendaylight.yangtools.yang.model.repo.spi;
 import com.google.common.annotations.Beta;
 import com.google.common.base.Preconditions;
 
+import org.opendaylight.yangtools.objcache.ObjectCache;
+import org.opendaylight.yangtools.objcache.ObjectCacheFactory;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
 
@@ -62,6 +64,7 @@ public final class PotentialSchemaSource<T extends SchemaSourceRepresentation> {
         }
     }
 
+    private static final ObjectCache CACHE = ObjectCacheFactory.getObjectCache(PotentialSchemaSource.class);
     private final Class<? extends T> representation;
     private final SourceIdentifier sourceIdentifier;
     private final int cost;
@@ -77,6 +80,15 @@ public final class PotentialSchemaSource<T extends SchemaSourceRepresentation> {
         return new PotentialSchemaSource<>(sourceIdentifier, representation, cost);
     }
 
+    /**
+     * Return a cached reference to an object equal to this object.
+     *
+     * @return A potentially shared reference, not guaranteed to be unique.
+     */
+    public PotentialSchemaSource<T> cachedReference() {
+        return CACHE.getReference(this);
+    }
+
     public SourceIdentifier getSourceIdentifier() {
         return sourceIdentifier;
     }
index 722ac918e08c995d391546d085fcf6b4aa36b95b..d6058292afb5ad4646d0fc6e31ca96673e207195 100644 (file)
@@ -153,14 +153,16 @@ public abstract class AbstractSchemaRepository implements SchemaRepository, Sche
 
     @Override
     public <T extends SchemaSourceRepresentation> SchemaSourceRegistration<T> registerSchemaSource(final SchemaSourceProvider<? super T> provider, final PotentialSchemaSource<T> source) {
-        final AbstractSchemaSourceRegistration<T> ret = new AbstractSchemaSourceRegistration<T>(provider, source) {
+        final PotentialSchemaSource<T> src = source.cachedReference();
+
+        final AbstractSchemaSourceRegistration<T> ret = new AbstractSchemaSourceRegistration<T>(provider, src) {
             @Override
             protected void removeRegistration() {
-                removeSource(source, this);
+                removeSource(src, this);
             }
         };
 
-        addSource(source, ret);
+        addSource(src, ret);
         return ret;
     }
 
index 9de7ce02b4fa5b077b642d1e5214db151b4c7f12..fdaaf33d8865c085dec2ee0c34526962d7f67f14 100644 (file)
@@ -70,6 +70,7 @@ import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.When_stmtContext;
 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Yang_version_stmtContext;
 import org.opendaylight.yangtools.antlrv4.code.gen.YangParserBaseListener;
 import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
@@ -556,13 +557,13 @@ public final class YangParserListenerImpl extends YangParserBaseListener {
     private QName parseQName(final String qnameString, final int line) {
         final QName qname;
         if (qnameString.indexOf(':') == -1) {
-            qname = QName.create(moduleQName.getNamespace(), moduleQName.getRevision(), qnameString);
+            qname = QName.create(moduleQName, qnameString);
         } else {
             final Iterator<String> split = COLON_SPLITTER.split(qnameString).iterator();
             final String prefix = split.next();
             final String name = split.next();
             if (prefix.equals(moduleBuilder.getPrefix())) {
-                qname = QName.create(moduleQName.getNamespace(), moduleQName.getRevision(), name);
+                qname = QName.create(moduleQName, name);
             } else {
                 ModuleImport imp = moduleBuilder.getImport(prefix);
                 if (imp == null) {
@@ -581,9 +582,14 @@ public final class YangParserListenerImpl extends YangParserBaseListener {
                     revision = namespaces.lastEntry().getKey();
                     namespace = namespaces.lastEntry().getValue();
                 } else {
+                    // FIXME: this lookup does not look right, as we will end up with
+                    //        a qname which does not have a namespace. At any rate we
+                    //        should arrive at a QNameModule!
                     namespace = namespaces.get(revision);
                 }
-                qname = QName.create(namespace, revision, name);
+
+                final QNameModule mod = QNameModule.cachedReference(QNameModule.create(namespace, revision));
+                qname = QName.create(mod, name);
             }
         }
         return qname;
index 3281f15acf9ef82fc639544aa0c691221ad7b955..3c8a45e26dca59f7f100b8f7fbc4f4289af54e73 100644 (file)
@@ -134,10 +134,10 @@ final class SharedSchemaContextFactory implements SchemaContextFactory {
 
             @Override
             public void onFailure(final Throwable t) {
-                LOG.info("Failed to assemble sources", t);
+                LOG.debug("Failed to assemble sources", t);
             }
         });
 
         return Futures.makeChecked(cf, MAPPER);
     }
-}
\ No newline at end of file
+}