Merge "Added Nonnull annotation for get operation"
authorRobert Varga <rovarga@cisco.com>
Mon, 15 Sep 2014 09:39:03 +0000 (09:39 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Mon, 15 Sep 2014 09:39:03 +0000 (09:39 +0000)
97 files changed:
benchmarks/pom.xml
benchmarks/src/main/java/org/opendaylight/yangtools/yang/data/impl/tree/InMemoryDataTreeBenchmark.java
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
code-generator/binding-java-api-generator/src/main/java/org/opendaylight/yangtools/sal/java/api/generator/BaseTemplate.xtend
code-generator/binding-java-api-generator/src/test/java/org/opendaylight/yangtools/sal/java/api/generator/test/EndodingInJavaDocTest.java [new file with mode: 0644]
code-generator/binding-java-api-generator/src/test/resources/compilation/encoding-javadoc/encoding-javadoc.yang [new file with mode: 0644]
common/features/pom.xml
common/features/src/main/resources/features.xml
common/parent/pom.xml
common/util/src/main/java/org/opendaylight/yangtools/util/ConcurrentDurationStatisticsTracker.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/DurationStatisticsTracker.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/DurationStatsTracker.java
common/util/src/main/java/org/opendaylight/yangtools/util/DurationWithTime.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/SynchronizedDurationStatsTracker.java [new file with mode: 0644]
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
pom.xml
restconf/restconf-util/src/main/java/org/opendaylight/yangtools/restconf/utils/RestconfUtils.java
yang/yang-binding/pom.xml
yang/yang-binding/src/test/java/org/opendaylight/yangtools/yang/binding/util/BindingReflectionsTest.java [new file with mode: 0644]
yang/yang-binding/src/test/java/org/opendaylight/yangtools/yang/binding/util/DataObjectReadingUtilTest.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/QName.java
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/QNameModule.java
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/YangInstanceIdentifier.java
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/SynchronizedDataTreeModification.java [new file with mode: 0644]
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/codec/SchemaTracker.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/RandomPrefix.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/XMLStreamNormalizedNodeStreamWriter.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/XmlDocumentUtils.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/XmlStreamUtils.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/AbstractImmutableDataContainerNode.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTree.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTreeModification.java
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/XmlStreamUtilsTest.java
yang/yang-model-api/pom.xml
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/SchemaPath.java
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-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/InMemorySchemaSourceCache.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 e80bfee9f9e19757899026c7319b8d0b70dd55b6..83805cf10dc91f42dc5427f1f07e92dbc4c6b340 100644 (file)
           <target>${java.source.version}</target>
         </configuration>
       </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>exec-maven-plugin</artifactId>
+        <configuration>
+          <classpathScope>test</classpathScope>
+          <executable>java</executable>
+          <arguments>
+            <argument>-classpath</argument>
+            <classpath/>
+            <argument>org.openjdk.jmh.Main</argument>
+            <argument>.*</argument>
+          </arguments>
+        </configuration>
+        <executions>
+          <execution>
+            <id>run-benchmarks</id>
+            <phase>integration-test</phase>
+            <goals>
+              <goal>exec</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
     </plugins>
   </build>
 </project>
index a9171687cfc29b5aed112cc81651abc9f0817daf..4cf95a73acb889b5ef9f066661f85c33ba63452b 100644 (file)
@@ -38,8 +38,12 @@ import org.openjdk.jmh.runner.options.OptionsBuilder;
 @State(Scope.Thread)
 @BenchmarkMode(Mode.AverageTime)
 @OutputTimeUnit(TimeUnit.MILLISECONDS)
+@Fork(1)
 public class InMemoryDataTreeBenchmark {
 
+    private static final int WARMUP_ITERATIONS = 20;
+    private static final int MEASUREMENT_ITERATIONS = 20;
+
     private static final int OUTER_LIST_100K = 100000;
     private static final int OUTER_LIST_50K = 50000;
     private static final int OUTER_LIST_10K = 10000;
@@ -139,8 +143,8 @@ public class InMemoryDataTreeBenchmark {
     }
 
     @Benchmark
-    @Warmup(iterations = 10, timeUnit = TimeUnit.MILLISECONDS)
-    @Measurement(iterations = 20, timeUnit = TimeUnit.MILLISECONDS)
+    @Warmup(iterations = WARMUP_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS)
+    @Measurement(iterations = MEASUREMENT_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS)
     public void write100KSingleNodeWithOneInnerItemInOneCommitBenchmark() throws Exception {
         final DataTreeSnapshot snapshot = datastore.takeSnapshot();
         final DataTreeModification modification = snapshot.newModification();
@@ -153,8 +157,8 @@ public class InMemoryDataTreeBenchmark {
     }
 
     @Benchmark
-    @Warmup(iterations = 10, timeUnit = TimeUnit.MILLISECONDS)
-    @Measurement(iterations = 20, timeUnit = TimeUnit.MILLISECONDS)
+    @Warmup(iterations = WARMUP_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS)
+    @Measurement(iterations = MEASUREMENT_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS)
     public void write100KSingleNodeWithOneInnerItemInCommitPerWriteBenchmark() throws Exception {
         final DataTreeSnapshot snapshot = datastore.takeSnapshot();
         for (int outerListKey = 0; outerListKey < OUTER_LIST_100K; ++outerListKey) {
@@ -167,8 +171,8 @@ public class InMemoryDataTreeBenchmark {
     }
 
     @Benchmark
-    @Warmup(iterations = 10, timeUnit = TimeUnit.MILLISECONDS)
-    @Measurement(iterations = 20, timeUnit = TimeUnit.MILLISECONDS)
+    @Warmup(iterations = WARMUP_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS)
+    @Measurement(iterations = MEASUREMENT_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS)
     public void write50KSingleNodeWithTwoInnerItemsInOneCommitBenchmark() throws Exception {
         final DataTreeSnapshot snapshot = datastore.takeSnapshot();
         final DataTreeModification modification = snapshot.newModification();
@@ -181,8 +185,8 @@ public class InMemoryDataTreeBenchmark {
     }
 
     @Benchmark
-    @Warmup(iterations = 10, timeUnit = TimeUnit.MILLISECONDS)
-    @Measurement(iterations = 20, timeUnit = TimeUnit.MILLISECONDS)
+    @Warmup(iterations = WARMUP_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS)
+    @Measurement(iterations = MEASUREMENT_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS)
     public void write50KSingleNodeWithTwoInnerItemsInCommitPerWriteBenchmark() throws Exception {
         final DataTreeSnapshot snapshot = datastore.takeSnapshot();
         for (int outerListKey = 0; outerListKey < OUTER_LIST_50K; ++outerListKey) {
@@ -195,8 +199,8 @@ public class InMemoryDataTreeBenchmark {
     }
 
     @Benchmark
-    @Warmup(iterations = 10, timeUnit = TimeUnit.MILLISECONDS)
-    @Measurement(iterations = 20, timeUnit = TimeUnit.MILLISECONDS)
+    @Warmup(iterations = WARMUP_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS)
+    @Measurement(iterations = MEASUREMENT_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS)
     public void write10KSingleNodeWithTenInnerItemsInOneCommitBenchmark() throws Exception {
         final DataTreeSnapshot snapshot = datastore.takeSnapshot();
         final DataTreeModification modification = snapshot.newModification();
@@ -209,8 +213,8 @@ public class InMemoryDataTreeBenchmark {
     }
 
     @Benchmark
-    @Warmup(iterations = 10, timeUnit = TimeUnit.MILLISECONDS)
-    @Measurement(iterations = 20, timeUnit = TimeUnit.MILLISECONDS)
+    @Warmup(iterations = WARMUP_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS)
+    @Measurement(iterations = MEASUREMENT_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS)
     public void write10KSingleNodeWithTenInnerItemsInCommitPerWriteBenchmark() throws Exception {
         final DataTreeSnapshot snapshot = datastore.takeSnapshot();
         for (int outerListKey = 0; outerListKey < OUTER_LIST_10K; ++outerListKey) {
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 c258e12092b2188c444203470e6131430e1b4824..26c95d2fa3236738d775797a41816f4c458adaf8 100644 (file)
@@ -157,9 +157,7 @@ abstract class BaseTemplate {
     def protected CharSequence asJavadoc(String comment) {
         if(comment == null) return ''
         var txt = comment
-        if (txt.contains("*/")) {
-            txt = txt.replace("*/", "&#42;&#47;")
-        }
+
         txt = comment.trim
         txt = formatToParagraph(txt)
 
@@ -189,7 +187,7 @@ abstract class BaseTemplate {
     }
 
     def protected String formatDataForJavaDoc(GeneratedType type) {
-        val typeDescription = type.getDescription();
+        val typeDescription = type.getDescription().encodeJavadocSymbols;
 
         return '''
             Â«IF !typeDescription.nullOrEmpty»
@@ -198,6 +196,13 @@ abstract class BaseTemplate {
         '''.toString
     }
 
+    def encodeJavadocSymbols(String description) {
+        if (!description.nullOrEmpty) {
+            return description.replace("*/", "&#42;&#47;")
+        }
+        return description;
+    }
+
     def asLink(String text) {
         val StringBuilder sb = new StringBuilder()
         var tempText = text
@@ -230,7 +235,7 @@ abstract class BaseTemplate {
         var StringBuilder lineBuilder = new StringBuilder();
         var boolean isFirstElementOnNewLineEmptyChar = false;
 
-        formattedText = formattedText.replace("*/", "&#42;&#47;")
+        formattedText = formattedText.encodeJavadocSymbols
         formattedText = formattedText.replace(NEW_LINE, "")
         formattedText = formattedText.replace("\t", "")
         formattedText = formattedText.replaceAll(" +", " ");
@@ -254,8 +259,9 @@ abstract class BaseTemplate {
                 lineBuilder.setLength(0)
                 sb.append(NEW_LINE)
 
-                if(nextElement.toString == ' ')
+                if(nextElement.toString == ' ') {
                     isFirstElementOnNewLineEmptyChar = !isFirstElementOnNewLineEmptyChar;
+                }
             }
 
             if(isFirstElementOnNewLineEmptyChar) {
diff --git a/code-generator/binding-java-api-generator/src/test/java/org/opendaylight/yangtools/sal/java/api/generator/test/EndodingInJavaDocTest.java b/code-generator/binding-java-api-generator/src/test/java/org/opendaylight/yangtools/sal/java/api/generator/test/EndodingInJavaDocTest.java
new file mode 100644 (file)
index 0000000..80b073f
--- /dev/null
@@ -0,0 +1,43 @@
+package org.opendaylight.yangtools.sal.java.api.generator.test;
+
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.yangtools.sal.java.api.generator.test.CompilationTestUtils.COMPILER_OUTPUT_PATH;
+import static org.opendaylight.yangtools.sal.java.api.generator.test.CompilationTestUtils.FS;
+import static org.opendaylight.yangtools.sal.java.api.generator.test.CompilationTestUtils.GENERATOR_OUTPUT_PATH;
+import static org.opendaylight.yangtools.sal.java.api.generator.test.CompilationTestUtils.cleanUp;
+import static org.opendaylight.yangtools.sal.java.api.generator.test.CompilationTestUtils.getSourceFiles;
+import static org.opendaylight.yangtools.sal.java.api.generator.test.CompilationTestUtils.testCompilation;
+
+import java.io.File;
+import java.util.HashSet;
+import java.util.List;
+import org.junit.Test;
+import org.opendaylight.yangtools.sal.binding.model.api.Type;
+import org.opendaylight.yangtools.sal.java.api.generator.GeneratorJavaFile;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Test if generated classes from yang file is compilable, generated javadoc comments contains
+ * symbols as javadoc comment tag, which caused of compilation problem.
+ */
+public class EndodingInJavaDocTest extends BaseCompilationTest {
+
+    @Test
+    public void testAugmentToUsesInAugment() throws Exception {
+        final File sourcesOutputDir = new File(GENERATOR_OUTPUT_PATH + FS + "encoding-javadoc");
+        assertTrue("Failed to create test file '" + sourcesOutputDir + "'", sourcesOutputDir.mkdir());
+        final File compiledOutputDir = new File(COMPILER_OUTPUT_PATH + FS + "encoding-javadoc");
+        assertTrue("Failed to create test file '" + compiledOutputDir + "'", compiledOutputDir.mkdir());
+
+        final List<File> sourceFiles = getSourceFiles("/compilation/encoding-javadoc");
+        final SchemaContext context = parser.parseFiles(sourceFiles);
+        final List<Type> types = bindingGenerator.generateTypes(context);
+        final GeneratorJavaFile generator = new GeneratorJavaFile(new HashSet<>(types));
+        generator.generateToFile(sourcesOutputDir);
+
+        // Test if sources are compilable
+        testCompilation(sourcesOutputDir, compiledOutputDir);
+        cleanUp(sourcesOutputDir, compiledOutputDir);
+    }
+
+}
diff --git a/code-generator/binding-java-api-generator/src/test/resources/compilation/encoding-javadoc/encoding-javadoc.yang b/code-generator/binding-java-api-generator/src/test/resources/compilation/encoding-javadoc/encoding-javadoc.yang
new file mode 100644 (file)
index 0000000..c66cef9
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2013 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
+ */
+module encoding-javadoc {
+    yang-version 1;
+    namespace "urn:opendaylight:encoding-javadoc";
+    prefix "enc";
+
+    revision "2014-04-09" {
+    }
+
+    container cont1 {
+        description "/e.g. 1/*/*";
+        reference "RFC 6020 - http://tools.ietf.org/html/rfc6020";
+
+        list list1 {
+            description "/e.g. 1/*/*";
+            reference "RFC 6020 - http://tools.ietf.org/html/rfc6020";
+
+            key "topology-id";
+            leaf topology-id {
+                description "/e.g. 1/*/*";
+                reference "RFC 6020 - http://tools.ietf.org/html/rfc6020";
+                type int32;
+            }
+            uses link1;
+        }
+    }
+
+    grouping link1 {
+        list link1 {
+            description "/e.g. 1/*/*";
+            reference "RFC 6020 - http://tools.ietf.org/html/rfc6020";
+            key "link-id";
+            uses link-attributes;
+        }
+    }
+
+    grouping link-attributes {
+        leaf link-id {
+            description "/e.g. 1/*/*";
+            reference "RFC 6020 - http://tools.ietf.org/html/rfc6020";
+            type int8;
+        }
+    }
+
+    leaf inclusion-rulez {
+        description "/e.g. 1/*/*";
+        reference "RFC 6020 http://technet.com";
+
+        type string;
+    }
+
+    leaf inclusion-rule {
+        description "/e.g. 1/*/*";
+        default include;
+
+        type enumeration {
+            enum include {
+                description
+                    "/e.g. 1/*/*";
+            }
+            enum exclude {
+                description
+                    "/e.g. 1/*/*";
+            }
+        }
+    }
+}
index 46c91492d06b1553f59ec5af433aaeb9985da0c0..9ecaeb485288d219b096792e020863d4edb544b5 100644 (file)
             <groupId>org.opendaylight.yangtools.model</groupId>
             <artifactId>ietf-inet-types</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools.model</groupId>
+            <artifactId>ietf-restconf</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.opendaylight.yangtools.model</groupId>
             <artifactId>ietf-yang-types</artifactId>
             <groupId>com.google.code.gson</groupId>
             <artifactId>gson</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
 
         <!-- test the features.xml -->
         <dependency>
index 45f1a8de1bec4c3f71a5f4ff46a9e54b9af46b32..baba09124cca17d37796fa74e1f169066eb3cd3e 100644 (file)
@@ -24,6 +24,7 @@
         <bundle>mvn:org.opendaylight.yangtools.model/ietf-inet-types/${ietf.inet.types.version}</bundle>
         <bundle>mvn:org.opendaylight.yangtools.model/ietf-yang-types/${ietf.yang.types.version}</bundle>
         <bundle>mvn:org.opendaylight.yangtools.model/ietf-yang-types-20130715/${ietf.yang.types.20130715.version}</bundle>
+        <bundle>mvn:org.opendaylight.yangtools.model/ietf-restconf/${ietf.restconf.version}</bundle>
         <bundle>mvn:org.opendaylight.yangtools.model/yang-ext/${yang.ext.version}</bundle>
         <bundle>mvn:org.opendaylight.yangtools.model/opendaylight-l2-types/${opendaylight.l2.types.version}</bundle>
         <bundle>mvn:org.opendaylight.yangtools.model/ietf-topology/${ietf.topology.version}</bundle>
index 950a6ba9cb0cb81f5e9539f867650389824d9481..914d1973bfd16a04b0517e71ee51ababdb0753f5 100644 (file)
             <dependency>
                 <groupId>org.opendaylight.yangtools.model</groupId>
                 <artifactId>ietf-restconf</artifactId>
-                <version>2013.09.04.1-SNAPSHOT</version>
+                <version>${ietf.restconf.version}</version>
             </dependency>
             <dependency>
                 <groupId>org.opendaylight.yangtools.model</groupId>
             <dependency>
                 <groupId>org.javassist</groupId>
                 <artifactId>javassist</artifactId>
-                <version>3.17.1-GA</version>
+                <version>${javassist.version}</version>
             </dependency>
             <dependency>
                 <groupId>xml-apis</groupId>
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/ConcurrentDurationStatisticsTracker.java b/common/util/src/main/java/org/opendaylight/yangtools/util/ConcurrentDurationStatisticsTracker.java
new file mode 100644 (file)
index 0000000..a06abbe
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * 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;
+
+import com.google.common.primitives.UnsignedLong;
+import java.util.concurrent.atomic.AtomicLongFieldUpdater;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+
+/**
+ * Concurrent version of {@link DurationStatisticsTracker}.
+ */
+// TODO: once DurationStatsTracker is gone make this class final
+class ConcurrentDurationStatisticsTracker extends DurationStatisticsTracker {
+    private static final AtomicReferenceFieldUpdater<ConcurrentDurationStatisticsTracker, DurationWithTime> LONGEST_UPDATER =
+            AtomicReferenceFieldUpdater.newUpdater(ConcurrentDurationStatisticsTracker.class, DurationWithTime.class, "longest");
+    private static final AtomicReferenceFieldUpdater<ConcurrentDurationStatisticsTracker, DurationWithTime> SHORTEST_UPDATER =
+            AtomicReferenceFieldUpdater.newUpdater(ConcurrentDurationStatisticsTracker.class, DurationWithTime.class, "shortest");
+    private static final AtomicLongFieldUpdater<ConcurrentDurationStatisticsTracker> COUNT_UPDATER =
+            AtomicLongFieldUpdater.newUpdater(ConcurrentDurationStatisticsTracker.class, "count");
+    private static final AtomicLongFieldUpdater<ConcurrentDurationStatisticsTracker> SUM_UPDATER =
+            AtomicLongFieldUpdater.newUpdater(ConcurrentDurationStatisticsTracker.class, "sum");
+
+    private volatile long sum = 0;
+    private volatile long count = 0;
+    private volatile DurationWithTime longest = null;
+    private volatile DurationWithTime shortest = null;
+
+    ConcurrentDurationStatisticsTracker() {
+        // Hidden on purpose
+    }
+
+    @Override
+    public final void addDuration(final long duration) {
+        // First update the quick stats
+        SUM_UPDATER.addAndGet(this, duration);
+        COUNT_UPDATER.incrementAndGet(this);
+
+        /*
+         * Now the hairy 'min/max' things. The notion of "now" we cache,
+         * so the first time we use it, we do not call it twice. We populate
+         * it lazily, though.
+         *
+         * The longest/shortest stats both are encapsulated in an object,
+         * so we update them atomically and we minimize the number of volatile
+         * operations.
+         */
+        DurationWithTime current = shortest;
+        if (current == null || duration < current.getDuration()) {
+            final DurationWithTime newObj = new DurationWithTime(duration, System.currentTimeMillis());
+            while (!SHORTEST_UPDATER.weakCompareAndSet(this, current, newObj)) {
+                current = shortest;
+                if (current != null && duration >= current.getDuration()) {
+                    break;
+                }
+            }
+        }
+
+        current = longest;
+        if (current == null || duration > current.getDuration()) {
+            final DurationWithTime newObj = new DurationWithTime(duration, System.currentTimeMillis());
+            while (!LONGEST_UPDATER.weakCompareAndSet(this, current, newObj)) {
+                current = longest;
+                if (current != null && duration <= current.getDuration()) {
+                    break;
+                }
+            }
+        }
+    }
+
+    @Override
+    public final long getTotalDurations() {
+        return count;
+    }
+
+    @Override
+    public final double getAverageDuration() {
+        final long myCount = count;
+        return myCount == 0 ? 0 : UnsignedLong.fromLongBits(sum).doubleValue() / myCount;
+    }
+
+    @Override
+    public final synchronized void reset() {
+        // Synchronized is just to make sure we do not have concurrent resets :)
+        longest = null;
+        shortest = null;
+        count = 0;
+        sum = 0;
+    }
+
+    @Override
+    protected final DurationWithTime getLongest() {
+        return longest;
+    }
+
+    @Override
+    protected final DurationWithTime getShortest() {
+        return shortest;
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/DurationStatisticsTracker.java b/common/util/src/main/java/org/opendaylight/yangtools/util/DurationStatisticsTracker.java
new file mode 100644 (file)
index 0000000..10cb39c
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2014 Brocade Communications 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;
+
+import static java.util.concurrent.TimeUnit.MICROSECONDS;
+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.annotations.Beta;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.util.Date;
+import java.util.concurrent.TimeUnit;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Abstract class that calculates and tracks time duration statistics.
+ *
+ * @author Thomas Pantelis
+ * @author Robert Varga
+ */
+@Beta
+public abstract class DurationStatisticsTracker {
+    private static final Logger LOG = LoggerFactory.getLogger(DurationStatisticsTracker.class);
+    private static final DecimalFormat DECIMAL_FORMAT;
+
+    static {
+        final DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance();
+        symbols.setDecimalSeparator('.');
+        DECIMAL_FORMAT = new DecimalFormat("0.00", symbols);
+    }
+
+    /**
+     * Create a concurrent {@link DurationStatisticsTracker}, which performs well
+     * in very contended environments.
+     *
+     * @return A new instance.
+     */
+    public static DurationStatisticsTracker createConcurrent() {
+        return new ConcurrentDurationStatisticsTracker();
+    }
+
+    /**
+     * Create a synchronized {@link DurationStatisticsTracker}, which performs well
+     * in non-contended environments.
+     *
+     * @return A new instance.
+     */
+    public static DurationStatisticsTracker createSynchronized() {
+        return new SynchronizedDurationStatsTracker();
+    }
+
+    /**
+     * Add a duration to track.
+     *
+     * @param duration
+     *            non-negative duration in nanoseconds.
+     */
+    public abstract void addDuration(long duration);
+
+    /**
+     * Returns the average duration in nanoseconds.
+     */
+    public abstract double getAverageDuration();
+
+    /**
+     * Returns the total number of tracked durations.
+     *
+     * @return Total number of measurements accumulated since last
+     *         {@link #reset()}.
+     */
+    public abstract long getTotalDurations();
+
+    /**
+     * Resets all statistics back to their defaults.
+     */
+    public abstract void reset();
+
+    /**
+     * Get the shortest recorded duration and the time when it was recorded.
+     *
+     * @return Duration and timestamp.
+     */
+    protected abstract DurationWithTime getShortest();
+
+    /**
+     * Get the longest recorded duration and the time when it was recorded.
+     *
+     * @return Duration and timestamp.
+     */
+    protected abstract DurationWithTime getLongest();
+
+    /**
+     * Returns the longest duration in nanoseconds.
+     */
+    public final long getLongestDuration() {
+        return getDuration(getLongest());
+    }
+
+    /**
+     * Returns the shortest duration in nanoseconds.
+     */
+    public final long getShortestDuration() {
+        return getDuration(getShortest());
+    }
+
+    /**
+     * Returns the average duration as a displayable String with units, e.g.
+     * "12.34 ms".
+     */
+    public final String getDisplayableAverageDuration() {
+        return formatDuration(getAverageDuration(), null);
+    }
+
+    /**
+     * 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 final String getDisplayableLongestDuration() {
+        return formatDuration(getLongest());
+    }
+
+    /**
+     * 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 final String getDisplayableShortestDuration() {
+        return formatDuration(getShortest());
+    }
+
+    /**
+     * Returns the time stamp of the longest duration.
+     */
+    public final long getTimeOfLongestDuration() {
+        return getTimeMillis(getLongest());
+    }
+
+    /**
+     * Returns the time stamp of the shortest duration.
+     */
+    public final long getTimeOfShortestDuration() {
+        return getTimeMillis(getShortest());
+    }
+
+    /**
+     * Returns formatted value of number, e.g. "12.34". Always is used dot as
+     * decimal separator.
+     */
+    private static synchronized String formatDecimalValue(final double value) {
+        return DECIMAL_FORMAT.format(value);
+    }
+
+    private static long getDuration(final DurationWithTime current) {
+        return current == null ? 0L : current.getDuration();
+    }
+
+    private static long getTimeMillis(final DurationWithTime current) {
+        return current == null ? 0L : current.getTimeMillis();
+    }
+
+    private static String formatDuration(final double duration, final Long timeStamp) {
+        final TimeUnit unit = chooseUnit((long) duration);
+        final double value = duration / NANOSECONDS.convert(1, unit);
+
+        final StringBuilder sb = new StringBuilder();
+        sb.append(formatDecimalValue(value));
+        sb.append(' ');
+        sb.append(abbreviate(unit));
+
+        if (timeStamp != null) {
+            sb.append(String.format(" at %1$tD %1$tT", new Date(timeStamp)));
+        }
+
+        return sb.toString();
+    }
+
+    private static String formatDuration(final DurationWithTime current) {
+        if (current != null) {
+            return formatDuration(current.getDuration(), current.getTimeMillis());
+        } else {
+            return formatDuration(0, null);
+        }
+    }
+
+    private static TimeUnit chooseUnit(final long nanos) {
+        // TODO: this could be inlined, as we are doing needless divisions
+        if (NANOSECONDS.toSeconds(nanos) > 0) {
+            return SECONDS;
+        }
+        if (NANOSECONDS.toMillis(nanos) > 0) {
+            return MILLISECONDS;
+        }
+        if (NANOSECONDS.toMicros(nanos) > 0) {
+            return MICROSECONDS;
+        }
+        return NANOSECONDS;
+    }
+
+    private static String abbreviate(final TimeUnit unit) {
+        switch (unit) {
+        case NANOSECONDS:
+            return "ns";
+        case MICROSECONDS:
+            return "\u03bcs"; // Î¼s
+        case MILLISECONDS:
+            return "ms";
+        case SECONDS:
+            return "s";
+        case MINUTES:
+            return "m";
+        case HOURS:
+            return "h";
+        case DAYS:
+            return "d";
+        }
+
+        LOG.warn("Unhandled time unit {}", unit);
+        return "";
+    }
+}
index 21690c2864e705817a6245dc2c9ec98ac1b21675..ca85e83d9554fa86300aa1ab75a993fadc4db06e 100644 (file)
  * 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;
 
-import static java.util.concurrent.TimeUnit.MICROSECONDS;
-import static java.util.concurrent.TimeUnit.MILLISECONDS;
-import static java.util.concurrent.TimeUnit.NANOSECONDS;
-import static java.util.concurrent.TimeUnit.SECONDS;
-
-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.
  *
  * @author Thomas Pantelis
+ * @author Robert Varga
+ *
+ * @deprecated Use {@link DurationStatisticsTracker} instead.
  */
-public class DurationStatsTracker {
-
-    private final AtomicLong totalDurations = new AtomicLong();
-    private final AtomicLong longestDuration = new AtomicLong();
-    private volatile long timeOfLongestDuration;
-    private final AtomicLong shortestDuration = new AtomicLong(Long.MAX_VALUE);
-    private volatile long timeOfShortestDuration;
-    private final AtomicDouble averageDuration = new AtomicDouble();
-
-    /**
-     * Add a duration to track.
-     *
-     * @param duration the duration in nanoseconds.
-     */
-    public void addDuration(long duration) {
-
-        double currentAve = averageDuration.get();
-        long currentTotal = totalDurations.get();
-
-        long newTotal = currentTotal + 1;
-
-        // Calculate moving cumulative average.
-        double newAve = currentAve * (double)currentTotal / (double)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 )) {
-                timeOfLongestDuration = System.currentTimeMillis();
-            }
-        }
-
-        long shortest = shortestDuration.get();
-        if( duration < shortest ) {
-            if(shortestDuration.compareAndSet( shortest, duration )) {
-                timeOfShortestDuration = System.currentTimeMillis();
-            }
-        }
-    }
-
-    /**
-     * Returns the total number of tracked durations.
-     */
-    public long getTotalDurations() {
-        return totalDurations.get();
-    }
-
-    /**
-     * Returns the longest duration in nanoseconds.
-     */
-    public long getLongestDuration() {
-        return longestDuration.get();
-    }
-
-    /**
-     * Returns the shortest duration in nanoseconds.
-     */
-    public long getShortestDuration() {
-        long shortest = shortestDuration.get();
-        return shortest < Long.MAX_VALUE ? shortest : 0;
-    }
-
+@Deprecated
+public class DurationStatsTracker extends ConcurrentDurationStatisticsTracker {
     /**
-     * Returns the average duration in nanoseconds.
+     * @deprecated Use {@link DurationStatisticsTracker#createConcurrent() instead.
      */
-    public double getAverageDuration() {
-        return averageDuration.get();
-    }
-
-    /**
-     * Returns the time stamp of the longest duration.
-     */
-    public long getTimeOfLongestDuration() {
-        return timeOfLongestDuration;
-    }
+    @Deprecated
+    public DurationStatsTracker() {
 
-    /**
-     * Returns the time stamp of the shortest duration.
-     */
-    public long getTimeOfShortestDuration() {
-        return timeOfShortestDuration;
-    }
-
-    /**
-     * Resets all statistics back to their defaults.
-     */
-    public void reset() {
-        totalDurations.set(0);
-        longestDuration.set(0);
-        timeOfLongestDuration = 0;
-        shortestDuration.set(Long.MAX_VALUE);
-        timeOfShortestDuration = 0;
-        averageDuration.set(0.0);
-    }
-
-    /**
-     * 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".
-     */
-    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".
-     */
-    public String getDisplayableLongestDuration() {
-        return formatDuration(getLongestDuration(), getTimeOfLongestDuration());
-    }
-
-    private String formatDuration(double duration, long timeStamp) {
-        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));
-    }
-
-    private static TimeUnit chooseUnit(long nanos) {
-        if(SECONDS.convert(nanos, NANOSECONDS) > 0) {
-            return SECONDS;
-        }
-
-        if(MILLISECONDS.convert(nanos, NANOSECONDS) > 0) {
-            return MILLISECONDS;
-        }
-
-        if(MICROSECONDS.convert(nanos, NANOSECONDS) > 0) {
-            return MICROSECONDS;
-        }
-
-        return NANOSECONDS;
-    }
+    // Remove once the no-argument constructor is removed
+    DurationStatsTracker(final Void dummy) {
 
-    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 "";
-        }
     }
 }
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/DurationWithTime.java b/common/util/src/main/java/org/opendaylight/yangtools/util/DurationWithTime.java
new file mode 100644 (file)
index 0000000..aa77678
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * 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;
+
+/**
+ * Utility holder for a duration/time of occurance.
+ */
+final class DurationWithTime {
+    private final long duration;
+    private final long timeMillis;
+
+    DurationWithTime(final long duration, final long timeMillis) {
+        this.duration = duration;
+        this.timeMillis = timeMillis;
+    }
+
+    long getDuration() {
+        return duration;
+    }
+
+    long getTimeMillis() {
+        return timeMillis;
+    }
+}
\ No newline at end of file
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/SynchronizedDurationStatsTracker.java b/common/util/src/main/java/org/opendaylight/yangtools/util/SynchronizedDurationStatsTracker.java
new file mode 100644 (file)
index 0000000..b3e9007
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * 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;
+
+import com.google.common.primitives.UnsignedLong;
+
+/**
+ * Non-concurrent implementation, useful for non-contended cases.
+ */
+final class SynchronizedDurationStatsTracker extends DurationStatisticsTracker {
+    private static final long NOT_SET = -1;
+
+    // Hot fields in the order in which they are accessed
+    private long durationSum = 0;
+    private long durationCount = 0;
+    private long shortestDuration = NOT_SET;
+    private long longestDuration = NOT_SET;
+
+    // Cold fields, longest has a higher chance of being accessed
+    private long longestTimestamp;
+    private long shortestTimestamp;
+
+    SynchronizedDurationStatsTracker() {
+        // Hidden on purpose
+    }
+
+    @Override
+    public synchronized void addDuration(final long duration) {
+        durationSum += duration;
+        durationCount++;
+
+        if (duration < shortestDuration || shortestDuration == NOT_SET) {
+            shortestDuration = duration;
+            shortestTimestamp = System.currentTimeMillis();
+        }
+        if (duration > longestDuration) {
+            longestDuration = duration;
+            longestTimestamp = System.currentTimeMillis();
+        }
+    }
+
+    @Override
+    public synchronized double getAverageDuration() {
+        return durationCount == 0 ? 0 : UnsignedLong.fromLongBits(durationSum).doubleValue() / durationCount;
+    }
+
+    @Override
+    public synchronized long getTotalDurations() {
+        return durationCount;
+    }
+
+    @Override
+    public synchronized void reset() {
+        durationSum = 0;
+        durationCount = 0;
+        longestDuration = NOT_SET;
+        shortestDuration = NOT_SET;
+    }
+
+    @Override
+    protected synchronized DurationWithTime getShortest() {
+        return shortestDuration == NOT_SET ? null : new DurationWithTime(shortestDuration, shortestTimestamp);
+    }
+
+    @Override
+    protected synchronized DurationWithTime getLongest() {
+        return longestDuration == NOT_SET ? null : new DurationWithTime(longestDuration, longestTimestamp);
+    }
+}
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..b83a0953f1bc83449804f4fc2a98815e35967b8d 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.weakCompareAndSet(this, largest, size));
     }
 }
diff --git a/pom.xml b/pom.xml
index ab46986476808eb7474c9a677e40a9e7f5b91d24..a0e081e02b2165653c26a3288670ee2e6f552229 100644 (file)
--- a/pom.xml
+++ b/pom.xml
         <module>restconf</module>
         <module>websocket</module>
         <module>yang</module>
-        <module>benchmarks</module>
       <!-- module>third-party</module -->
     </modules>
 
+    <profiles>
+      <profile>
+        <id>benchmarks</id>
+        <activation>
+          <activeByDefault>false</activeByDefault>
+        </activation>
+        <modules>
+          <module>benchmarks</module>
+        </modules>
+      </profile>
+    </profiles>
+
     <build>
         <pluginManagement>        
             <plugins>
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 f4efbc3c7aeadadc5cd79f620ee463c49eeb98bb..ad339e8668b02542d6363eaf2bb11eb0597f2065 100644 (file)
             <artifactId>junit</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>
diff --git a/yang/yang-binding/src/test/java/org/opendaylight/yangtools/yang/binding/util/BindingReflectionsTest.java b/yang/yang-binding/src/test/java/org/opendaylight/yangtools/yang/binding/util/BindingReflectionsTest.java
new file mode 100644 (file)
index 0000000..694c807
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * 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.binding.util;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+import java.util.Collections;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class BindingReflectionsTest {
+
+    @Test
+    public void testBindingWithDummyObject() {
+        assertEquals("Package name should be equal to string", "org.opendaylight.yang.gen.v1.test.rev990939",
+                BindingReflections.getModelRootPackageName("org.opendaylight.yang.gen.v1.test.rev990939"));
+        assertEquals("ModuleInfoClassName should be equal to string", "test.$YangModuleInfoImpl",
+                BindingReflections.getModuleInfoClassName("test"));
+        assertEquals("Module info should be empty Set", Collections.EMPTY_SET,
+                BindingReflections.loadModuleInfos());
+        assertFalse("Should not be RpcType", BindingReflections.isRpcType(DataObject.class));
+        assertFalse("Should not be AugmentationChild", BindingReflections.isAugmentationChild(DataObject.class));
+        assertTrue("Should be BindingClass", BindingReflections.isBindingClass(DataObject.class));
+        assertFalse("Should not be Notification", BindingReflections.isNotification(DataObject.class));
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-binding/src/test/java/org/opendaylight/yangtools/yang/binding/util/DataObjectReadingUtilTest.java b/yang/yang-binding/src/test/java/org/opendaylight/yangtools/yang/binding/util/DataObjectReadingUtilTest.java
new file mode 100644 (file)
index 0000000..e2400f9
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * 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.binding.util;
+
+import com.google.common.collect.ImmutableMap;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.test.mock.Nodes;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import static org.junit.Assert.assertTrue;
+
+public class DataObjectReadingUtilTest {
+    @Mock private InstanceIdentifier<? extends DataObject> pathNull;
+    @Mock private Map.Entry<InstanceIdentifier<? extends DataObject>, DataObject> entryNull;
+    @Mock private DataObject mockedDataObject;
+    private InstanceIdentifier<? extends DataObject> path;
+    private Map.Entry<InstanceIdentifier<? extends DataObject>, DataObject> entry;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+
+        path = InstanceIdentifier.builder(Nodes.class).toInstance();
+        ImmutableMap map = ImmutableMap.<InstanceIdentifier<? extends DataObject>,
+                DataObject>builder().put(path, mockedDataObject).build();
+
+        Set entries = map.entrySet();
+        Iterator it = entries.iterator();
+        while(it.hasNext()) {
+            entry = (Map.Entry)it.next();
+        }
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testReadDataParentNull() {
+        DataObjectReadingUtil.readData(entryNull.getValue(), (InstanceIdentifier) entryNull.getKey(), pathNull);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testReadDataParentPathNull() {
+        DataObjectReadingUtil.readData(entry.getValue(), (InstanceIdentifier) entryNull.getKey(), pathNull);
+    }
+
+    @Test
+    public void testReadDataWithThreeParams() {
+        assertTrue("Check if contains key",
+                DataObjectReadingUtil.readData(entry.getValue(),
+                        (InstanceIdentifier) entry.getKey(), path).containsKey(entry.getKey()));
+
+        assertTrue("Check if contains value",
+                DataObjectReadingUtil.readData(entry.getValue(),
+                        (InstanceIdentifier) entry.getKey(), path).containsValue(entry.getValue()));
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testReadDataWithTwoParams() {
+        DataObjectReadingUtil.readData(mockedDataObject, DataObject.class);
+    }
+}
\ No newline at end of file
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);
     }
 
     /**
index bec56bf76fe77f54467ab5fc00e46d9f03eb1a59..452b2fa99e78965f5949a83801f738db137be34b 100644 (file)
@@ -11,7 +11,6 @@ import java.io.Serializable;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.Date;
-
 import org.opendaylight.yangtools.concepts.Immutable;
 import org.opendaylight.yangtools.objcache.ObjectCache;
 import org.opendaylight.yangtools.objcache.ObjectCacheFactory;
@@ -31,7 +30,7 @@ public final class QNameModule implements Immutable, Serializable {
     private final Date revision;
 
     //Nullable
-    private String formattedRevision;
+    private volatile String formattedRevision;
 
     private QNameModule(final URI namespace, final Date revision) {
         this.namespace = namespace;
@@ -68,15 +67,13 @@ public final class QNameModule implements Immutable, Serializable {
             return null;
         }
 
-        if (formattedRevision == null) {
-            synchronized (this) {
-                if (formattedRevision == null) {
-                    formattedRevision = SimpleDateFormatUtil.getRevisionFormat().format(revision);
-                }
-            }
+        String ret = formattedRevision;
+        if (ret == null) {
+            ret = SimpleDateFormatUtil.getRevisionFormat().format(revision);
+            formattedRevision = ret;
         }
 
-        return formattedRevision;
+        return ret;
     }
 
     /**
index 84ebdf0f2035f30446e0098220050a80e9e7399d..2916b88e8bfde2898622e3076b151f5e0b8421c5 100644 (file)
@@ -13,7 +13,6 @@ import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
-
 import java.io.Serializable;
 import java.lang.reflect.Array;
 import java.util.ArrayList;
@@ -25,7 +24,7 @@ import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Objects;
 import java.util.Set;
-
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
 import org.opendaylight.yangtools.concepts.Builder;
 import org.opendaylight.yangtools.concepts.Immutable;
 import org.opendaylight.yangtools.concepts.Path;
@@ -67,6 +66,11 @@ import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
  * @see http://tools.ietf.org/html/rfc6020#section-9.13
  */
 public final class YangInstanceIdentifier implements Path<YangInstanceIdentifier>, Immutable, Serializable {
+    @SuppressWarnings("rawtypes")
+    private static final AtomicReferenceFieldUpdater<YangInstanceIdentifier, ImmutableList> LEGACYPATH_UPDATER =
+            AtomicReferenceFieldUpdater.newUpdater(YangInstanceIdentifier.class, ImmutableList.class, "legacyPath");
+    private static final AtomicReferenceFieldUpdater<YangInstanceIdentifier, String> TOSTRINGCACHE_UPDATER =
+            AtomicReferenceFieldUpdater.newUpdater(YangInstanceIdentifier.class, String.class, "toStringCache");
     private static final YangInstanceIdentifier EMPTY = trustedCreate(Collections.<PathArgument>emptyList());
 
     private static final long serialVersionUID = 2L;
@@ -80,11 +84,10 @@ public final class YangInstanceIdentifier implements Path<YangInstanceIdentifier
         // Temporary variable saves a volatile read
         ImmutableList<PathArgument> ret = legacyPath;
         if (ret == null) {
-            synchronized (this) {
-                // We could have used a synchronized block, but let's just not bother
-                ret = ImmutableList.copyOf(pathArguments);
-                legacyPath = ret;
-            }
+            // We could have used a synchronized block, but the window is quite
+            // small and worst that can happen is duplicate object construction.
+            ret = ImmutableList.copyOf(pathArguments);
+            LEGACYPATH_UPDATER.lazySet(this, ret);
         }
 
         return ret;
@@ -354,6 +357,8 @@ public final class YangInstanceIdentifier implements Path<YangInstanceIdentifier
     }
 
     private static abstract class AbstractPathArgument implements PathArgument {
+        private static final AtomicReferenceFieldUpdater<AbstractPathArgument, Integer> HASH_UPDATER =
+                AtomicReferenceFieldUpdater.newUpdater(AbstractPathArgument.class, Integer.class, "hash");
         private static final long serialVersionUID = -4546547994250849340L;
         private final QName nodeType;
         private volatile transient Integer hash = null;
@@ -380,13 +385,8 @@ public final class YangInstanceIdentifier implements Path<YangInstanceIdentifier
         public final int hashCode() {
             Integer ret = hash;
             if (ret == null) {
-                synchronized (this) {
-                    ret = hash;
-                    if (ret == null) {
-                        ret = hashCodeImpl();
-                        hash = ret;
-                    }
-                }
+                ret = hashCodeImpl();
+                HASH_UPDATER.lazySet(this, ret);
             }
 
             return ret;
@@ -787,23 +787,18 @@ public final class YangInstanceIdentifier implements Path<YangInstanceIdentifier
          */
         String ret = toStringCache;
         if (ret == null) {
-            synchronized (this) {
-                ret = toStringCache;
-                if (ret == null) {
-                    final StringBuilder builder = new StringBuilder("/");
-                    PathArgument prev = null;
-                    for (PathArgument argument : getPathArguments()) {
-                        if (prev != null) {
-                            builder.append('/');
-                        }
-                        builder.append(argument.toRelativeString(prev));
-                        prev = argument;
-                    }
-
-                    ret = builder.toString();
-                    toStringCache = ret;
+            final StringBuilder builder = new StringBuilder("/");
+            PathArgument prev = null;
+            for (PathArgument argument : getPathArguments()) {
+                if (prev != null) {
+                    builder.append('/');
                 }
+                builder.append(argument.toRelativeString(prev));
+                prev = argument;
             }
+
+            ret = builder.toString();
+            TOSTRINGCACHE_UPDATER.lazySet(this, ret);
         }
         return ret;
     }
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/SynchronizedDataTreeModification.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/SynchronizedDataTreeModification.java
new file mode 100644 (file)
index 0000000..d78f6fa
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * 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.api.schema.tree;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * A {@link DataTreeModification} implementation which delegates all calls to
+ * another instance, making sure no method is being invoked from multiple threads
+ * concurrently.
+ */
+public final class SynchronizedDataTreeModification implements DataTreeModification {
+    private final DataTreeModification delegate;
+
+    private SynchronizedDataTreeModification(final DataTreeModification delegate) {
+        this.delegate = Preconditions.checkNotNull(delegate);
+    }
+
+    public DataTreeModification create(final DataTreeModification delegate) {
+        return new SynchronizedDataTreeModification(delegate);
+    }
+
+    @Override
+    public synchronized Optional<NormalizedNode<?, ?>> readNode(final YangInstanceIdentifier path) {
+        return delegate.readNode(path);
+    }
+
+    @Override
+    public synchronized DataTreeModification newModification() {
+        return delegate.newModification();
+    }
+
+    @Override
+    public synchronized void delete(final YangInstanceIdentifier path) {
+        delegate.delete(path);
+    }
+
+    @Override
+    public synchronized void merge(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+        delegate.merge(path, data);
+    }
+
+    @Override
+    public synchronized void write(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+        delegate.write(path, data);
+    }
+
+    @Override
+    public synchronized void ready() {
+        delegate.ready();
+    }
+}
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..8fcc596ab47ebe3e8e5d2ed86ecb00f8cc2d104e 100644 (file)
@@ -7,30 +7,22 @@
  */
 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 +34,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 +61,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 +74,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,233 +112,145 @@ 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.writeChildJsonIdentifier(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
     public void anyxmlNode(final NodeIdentifier name, final Object value) throws IOException {
+        @SuppressWarnings("unused")
         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.writeChildJsonIdentifier(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..a3ad80d
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * 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 child 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
+     */
+    final void writeChildJsonIdentifier(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("\":");
+    }
+
+    /**
+     * Write our 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 writeMyJsonIdentifier(final SchemaContext schema, final Writer writer, final QName qname) throws IOException {
+        parent.writeChildJsonIdentifier(schema, writer, qname);
+    }
+
+    /**
+     * 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..9c0be47
--- /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 {
+        writeMyJsonIdentifier(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..91c6ca7
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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 {
+        writeMyJsonIdentifier(schema, writer, getQName());
+        super.emitStart(schema, writer);
+    }
+}
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 39181ae49fbf93ae556d1a4e092b671f26cfb2f9..16018df8c3ad177dad6cb26166b860b628eee89f 100644 (file)
@@ -139,12 +139,12 @@ public final class SchemaTracker {
         return (LeafSchemaNode) schema;
     }
 
-    public SchemaNode startLeafSet(final NodeIdentifier name) {
+    public LeafListSchemaNode startLeafSet(final NodeIdentifier name) {
         final SchemaNode schema = getSchema(name);
 
         Preconditions.checkArgument(schema instanceof LeafListSchemaNode, "Node %s is not a leaf-list", schema.getPath());
         schemaStack.push(schema);
-        return schema;
+        return (LeafListSchemaNode)schema;
     }
 
     public LeafListSchemaNode leafSetEntryNode() {
@@ -154,22 +154,22 @@ public final class SchemaTracker {
         return (LeafListSchemaNode) parent;
     }
 
-    public SchemaNode startChoiceNode(final NodeIdentifier name) {
+    public ChoiceNode startChoiceNode(final NodeIdentifier name) {
         LOG.debug("Enter choice {}", name);
         final SchemaNode schema = getSchema(name);
 
         Preconditions.checkArgument(schema instanceof ChoiceNode, "Node %s is not a choice", schema.getPath());
         schemaStack.push(schema);
-        return schema;
+        return (ChoiceNode)schema;
     }
 
-    public SchemaNode startContainerNode(final NodeIdentifier name) {
+    public ContainerSchemaNode startContainerNode(final NodeIdentifier name) {
         LOG.debug("Enter container {}", name);
         final SchemaNode schema = getSchema(name);
 
         Preconditions.checkArgument(schema instanceof ContainerSchemaNode, "Node %s is not a container", schema.getPath());
         schemaStack.push(schema);
-        return schema;
+        return (ContainerSchemaNode)schema;
     }
 
     public AugmentationSchema startAugmentationNode(final AugmentationIdentifier identifier) {
index 518e796808d9676519e201d89db4dfabc1e90140..125ac23eb86fd057551e821e76fa757b0f2a36bf 100644 (file)
@@ -15,6 +15,7 @@ import com.google.common.collect.HashBiMap;
 import java.net.URI;
 import java.util.Map;
 
+import javax.xml.XMLConstants;
 import javax.xml.namespace.NamespaceContext;
 
 class RandomPrefix {
@@ -57,7 +58,12 @@ class RandomPrefix {
     }
 
     private boolean alreadyUsedPrefix(final String prefix) {
-        return context != null && context.getNamespaceURI(prefix) != null;
+        if (context == null) {
+            return false;
+        }
+
+        final String str = context.getNamespaceURI(prefix);
+        return !XMLConstants.NULL_NS_URI.equals(str);
     }
 
     @VisibleForTesting
index c24486707704a2e2fdb2cc5435d0503a0f68f3fb..97f676ae69bd62636764d138ea74e2ee4a678fb9 100644 (file)
@@ -7,14 +7,12 @@
  */
 package org.opendaylight.yangtools.yang.data.impl.codec.xml;
 
-import com.google.common.base.Preconditions;
+import static javax.xml.XMLConstants.DEFAULT_NS_PREFIX;
 
+import com.google.common.base.Preconditions;
 import java.io.IOException;
-
-import javax.xml.stream.XMLOutputFactory;
 import javax.xml.stream.XMLStreamException;
 import javax.xml.stream.XMLStreamWriter;
-
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
@@ -68,22 +66,25 @@ public final class XMLStreamNormalizedNodeStreamWriter implements NormalizedNode
      * @return A new {@link NormalizedNodeStreamWriter}
      */
     public static NormalizedNodeStreamWriter create(final XMLStreamWriter writer, final SchemaContext context, final SchemaPath path) {
-        final Boolean repairing = (Boolean) writer.getProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES);
-        Preconditions.checkArgument(repairing == true, "XML Stream Writer has to be repairing namespaces");
         return new XMLStreamNormalizedNodeStreamWriter(writer, context, path);
     }
 
-    private void writeElement(final QName qname, final TypeDefinition<?> type, final Object value) throws IOException {
-        final String ns = qname.getNamespace().toString();
+    private void writeStartElement( QName qname) throws XMLStreamException {
+        String ns = qname.getNamespace().toString();
+        String parentNs = writer.getNamespaceContext().getNamespaceURI(DEFAULT_NS_PREFIX);
+        writer.writeStartElement(DEFAULT_NS_PREFIX, qname.getLocalName(), ns);
+        if (!ns.equals(parentNs)) {
+            writer.writeDefaultNamespace(ns);
+        }
+    }
 
+    private void writeElement(final QName qname, final TypeDefinition<?> type, final Object value) throws IOException {
         try {
+            writeStartElement(qname);
             if (value != null) {
-                writer.writeStartElement(ns, qname.getLocalName());
                 UTILS.writeValue(writer, type, value);
-                writer.writeEndElement();
-            } else {
-                writer.writeEmptyElement(ns, qname.getLocalName());
             }
+            writer.writeEndElement();
         } catch (XMLStreamException e) {
             throw new IOException("Failed to emit element", e);
         }
@@ -91,7 +92,7 @@ public final class XMLStreamNormalizedNodeStreamWriter implements NormalizedNode
 
     private void startElement(final QName qname) throws IOException {
         try {
-            writer.writeStartElement(qname.getNamespace().toString(), qname.getLocalName());
+            writeStartElement(qname);
         } catch (XMLStreamException e) {
             throw new IOException("Failed to start element", e);
         }
@@ -169,16 +170,12 @@ public final class XMLStreamNormalizedNodeStreamWriter implements NormalizedNode
     public void anyxmlNode(final NodeIdentifier name, final Object value) throws IOException {
         final AnyXmlSchemaNode schema = tracker.anyxmlNode(name);
         final QName qname = schema.getQName();
-        final String ns = qname.getNamespace().toString();
-
         try {
+            writeStartElement(qname);
             if (value != null) {
-                writer.writeStartElement(ns, qname.getLocalName());
                 UTILS.writeValue(writer, (Node<?>)value, schema);
-                writer.writeEndElement();
-            } else {
-                writer.writeEmptyElement(ns, qname.getLocalName());
             }
+            writer.writeEndElement();
         } catch (XMLStreamException e) {
             throw new IOException("Failed to emit element", e);
         }
index d944842bf983ca534804929c2cd3b28143b2f577..d49d7e599d65f4f162360e37f647d4204d7a6717 100644 (file)
@@ -228,38 +228,30 @@ public class XmlDocumentUtils {
 
     protected static Node<?> toSimpleNodeWithType(final Element xmlElement, final LeafSchemaNode schema,
             final XmlCodecProvider codecProvider,final SchemaContext schemaCtx) {
-        final TypeDefinition<?> baseType = XmlUtils.resolveBaseTypeFrom(schema.getType());
-        final String text = xmlElement.getTextContent();
-        final Object value;
-
-        if (baseType instanceof InstanceIdentifierType) {
-            value = InstanceIdentifierForXmlCodec.deserialize(xmlElement, schemaCtx);
-        } else if (baseType instanceof IdentityrefTypeDefinition) {
-            value = InstanceIdentifierForXmlCodec.toIdentity(text, xmlElement, schemaCtx);
-        } else {
-            final TypeDefinitionAwareCodec<?, ?> codec = codecProvider.codecFor(schema.getType());
-            if (codec == null) {
-                LOG.info("No codec for schema {}, falling back to text", schema);
-                value = text;
-            } else {
-                value = codec.deserialize(text);
-            }
-        }
-
+        final Object value = resolveValueFromSchemaType(xmlElement, schema, schema.getType(), codecProvider, schemaCtx);
         Optional<ModifyAction> modifyAction = getModifyOperationFromAttributes(xmlElement);
         return new SimpleNodeTOImpl<>(schema.getQName(), null, value, modifyAction.orNull());
     }
 
     private static Node<?> toSimpleNodeWithType(final Element xmlElement, final LeafListSchemaNode schema,
             final XmlCodecProvider codecProvider,final SchemaContext schemaCtx) {
+        final Object value = resolveValueFromSchemaType(xmlElement, schema, schema.getType(), codecProvider, schemaCtx);
+        Optional<ModifyAction> modifyAction = getModifyOperationFromAttributes(xmlElement);
+        return new SimpleNodeTOImpl<>(schema.getQName(), null, value, modifyAction.orNull());
+    }
+
+    private static Object resolveValueFromSchemaType(final Element xmlElement, final DataSchemaNode schema, final TypeDefinition<?> type,
+        final XmlCodecProvider codecProvider,final SchemaContext schemaCtx) {
+        final TypeDefinition<?> baseType = XmlUtils.resolveBaseTypeFrom(type);
+        final String text = xmlElement.getTextContent();
         final Object value;
 
-        if (schema.getType() instanceof InstanceIdentifierType) {
+        if (baseType instanceof InstanceIdentifierType) {
             value = InstanceIdentifierForXmlCodec.deserialize(xmlElement, schemaCtx);
+        } else if (baseType instanceof IdentityrefTypeDefinition) {
+            value = InstanceIdentifierForXmlCodec.toIdentity(text, xmlElement, schemaCtx);
         } else {
-            final TypeDefinitionAwareCodec<?, ?> codec = codecProvider.codecFor(schema.getType());
-            final String text = xmlElement.getTextContent();
-
+            final TypeDefinitionAwareCodec<?, ?> codec = codecProvider.codecFor(type);
             if (codec == null) {
                 LOG.info("No codec for schema {}, falling back to text", schema);
                 value = text;
@@ -267,9 +259,7 @@ public class XmlDocumentUtils {
                 value = codec.deserialize(text);
             }
         }
-
-        Optional<ModifyAction> modifyAction = getModifyOperationFromAttributes(xmlElement);
-        return new SimpleNodeTOImpl<>(schema.getQName(), null, value, modifyAction.orNull());
+        return value;
     }
 
     private static Node<?> toCompositeNodeWithSchema(final Element xmlElement, final QName qName, final DataNodeContainer schema,
index 14b5c8593f5d85b68533d95bd496929653a03cd6..aa379dc963f11606150fe370339eb21895d7edb8 100644 (file)
@@ -3,15 +3,13 @@ package org.opendaylight.yangtools.yang.data.impl.codec.xml;
 import com.google.common.annotations.Beta;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
-
 import java.net.URI;
+import java.util.Map;
 import java.util.Map.Entry;
-
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 import javax.xml.stream.XMLStreamException;
 import javax.xml.stream.XMLStreamWriter;
-
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.AttributesContainer;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
@@ -93,7 +91,10 @@ public class XmlStreamUtils {
         final String str = XmlUtils.encodeIdentifier(prefixes, id);
 
         for (Entry<URI, String> e: prefixes.getPrefixes()) {
-            writer.writeNamespace(e.getValue(), e.getKey().toString());
+            final String ns = e.getKey().toString();
+            final String p = e.getValue();
+
+            writer.writeNamespace(p, ns);
         }
         writer.writeCharacters(str);
     }
@@ -144,7 +145,14 @@ public class XmlStreamUtils {
         final String ns = qname.getNamespace() != null ? qname.getNamespace().toString() : "";
 
         if (isEmptyElement(data)) {
-            writer.writeEmptyElement(pfx, qname.getLocalName(), ns);
+            if (hasAttributes(data)) {
+                writer.writeStartElement(pfx, qname.getLocalName(), ns);
+                final RandomPrefix randomPrefix = new RandomPrefix();
+                writeAttributes(writer, (AttributesContainer) data, randomPrefix);
+                writer.writeEndElement();
+            } else {
+                writer.writeEmptyElement(pfx, qname.getLocalName(), ns);
+            }
             return;
         }
 
@@ -163,11 +171,9 @@ public class XmlStreamUtils {
      * @throws XMLStreamException if an encoding problem occurs
      */
     public void writeValue(final XMLStreamWriter writer, final @Nonnull Node<?> data, final SchemaNode schema) throws XMLStreamException {
-        if (data instanceof AttributesContainer && ((AttributesContainer) data).getAttributes() != null) {
+        if (hasAttributes(data)) {
             RandomPrefix randomPrefix = new RandomPrefix();
-            for (Entry<QName, String> attribute : ((AttributesContainer) data).getAttributes().entrySet()) {
-                writeAttribute(writer, attribute, randomPrefix);
-            }
+            writeAttributes(writer, (AttributesContainer) data, randomPrefix);
         }
 
         if (data instanceof SimpleNode<?>) {
@@ -200,6 +206,21 @@ public class XmlStreamUtils {
         }
     }
 
+    private static void writeAttributes(final XMLStreamWriter writer, final AttributesContainer data, final RandomPrefix randomPrefix) throws XMLStreamException {
+        for (Entry<QName, String> attribute : data.getAttributes().entrySet()) {
+            writeAttribute(writer, attribute, randomPrefix);
+        }
+    }
+
+    private static boolean hasAttributes(final Node<?> data) {
+        if (data instanceof AttributesContainer) {
+            final Map<QName, String> c = ((AttributesContainer) data).getAttributes();
+            return c != null && !c.isEmpty();
+        } else {
+            return false;
+        }
+    }
+
     @VisibleForTesting
     static void writeAttribute(final XMLStreamWriter writer, final Entry<QName, String> attribute, final RandomPrefix randomPrefix)
             throws XMLStreamException {
@@ -247,6 +268,7 @@ public class XmlStreamUtils {
         }
     }
 
+    @SuppressWarnings("deprecation")
     private static void write(final @Nonnull XMLStreamWriter writer, final @Nonnull IdentityrefTypeDefinition type, final @Nonnull Object value) throws XMLStreamException {
         if (value instanceof QName) {
             final QName qname = (QName) value;
@@ -257,7 +279,8 @@ public class XmlStreamUtils {
                 prefix = "x";
             }
 
-            writer.writeNamespace(prefix, qname.getNamespace().toString());
+            final String ns = qname.getNamespace().toString();
+            writer.writeNamespace(prefix, ns);
             writer.writeCharacters(prefix + ':' + qname.getLocalName());
         } else {
             LOG.debug("Value of {}:{} is not a QName but {}", type.getQName().getNamespace(), type.getQName().getLocalName(), value.getClass());
@@ -269,7 +292,7 @@ public class XmlStreamUtils {
         if (value instanceof YangInstanceIdentifier) {
             write(writer, (YangInstanceIdentifier)value);
         } else {
-            LOG.debug("Value of {}:{} is not an InstanceIdentifier but {}", type.getQName().getNamespace(), type.getQName().getLocalName(), value.getClass());
+            LOG.warn("Value of {}:{} is not an InstanceIdentifier but {}", type.getQName().getNamespace(), type.getQName().getLocalName(), value.getClass());
             writer.writeCharacters(String.valueOf(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 0f0b4a12da7e96f594a5d7a8f78c1a0dc79bf8c4..b8344d896387f9cbc7c35ebf785f6a5d681bfaf8 100644 (file)
@@ -7,9 +7,10 @@
  */
 package org.opendaylight.yangtools.yang.data.impl.schema.tree;
 
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
-
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
@@ -22,9 +23,6 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-
 /**
  * Read-only snapshot of the data tree.
  */
@@ -130,7 +128,7 @@ final class InMemoryDataTree implements DataTree {
         rwLock.writeLock().lock();
         try {
             Preconditions.checkState(c.getBeforeRoot() == rootNode,
-                    String.format("Store tree %s and candidate base %s differ.", rootNode, c.getBeforeRoot()));
+                    "Store tree %s and candidate base %s differ.", rootNode, c.getBeforeRoot());
             this.rootNode = c.getAfterRoot();
         } finally {
             rwLock.writeLock().unlock();
index de4bcef79666b6002ede5051ed2064a763838843..9e0c60f6a091bad054cd1c35c8b2630346be71c9 100644 (file)
@@ -7,10 +7,10 @@
  */
 package org.opendaylight.yangtools.yang.data.impl.schema.tree;
 
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
 import java.util.Map.Entry;
-
-import javax.annotation.concurrent.GuardedBy;
-
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
@@ -23,32 +23,31 @@ import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-
 final class InMemoryDataTreeModification implements DataTreeModification {
+    private static final AtomicIntegerFieldUpdater<InMemoryDataTreeModification> UPDATER =
+            AtomicIntegerFieldUpdater.newUpdater(InMemoryDataTreeModification.class, "sealed");
     private static final Logger LOG = LoggerFactory.getLogger(InMemoryDataTreeModification.class);
+
     private final RootModificationApplyOperation strategyTree;
     private final InMemoryDataTreeSnapshot snapshot;
     private final ModifiedNode rootNode;
     private final Version version;
 
-    @GuardedBy("this")
-    private boolean sealed = false;
+    private volatile int sealed = 0;
 
     InMemoryDataTreeModification(final InMemoryDataTreeSnapshot snapshot, final RootModificationApplyOperation resolver) {
         this.snapshot = Preconditions.checkNotNull(snapshot);
         this.strategyTree = Preconditions.checkNotNull(resolver).snapshot();
         this.rootNode = ModifiedNode.createUnmodified(snapshot.getRootNode(), false);
+
         /*
          * We could allocate version beforehand, since Version contract
-         * states two allocated version must be allways different.
-         * 
+         * states two allocated version must be always different.
+         *
          * Preallocating version simplifies scenarios such as
          * chaining of modifications, since version for particular
          * node in modification and in data tree (if successfully
-         * commited) will be same and will not change.
-         * 
+         * committed) will be same and will not change.
          */
         this.version = snapshot.getRootNode().getSubtreeVersion().next();
     }
@@ -62,20 +61,21 @@ final class InMemoryDataTreeModification implements DataTreeModification {
     }
 
     @Override
-    public synchronized void write(final YangInstanceIdentifier path, final NormalizedNode<?, ?> value) {
+    public void write(final YangInstanceIdentifier path, final NormalizedNode<?, ?> value) {
         checkSealed();
+
         resolveModificationFor(path).write(value);
     }
 
     @Override
-    public synchronized void merge(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+    public void merge(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
         checkSealed();
+
         mergeImpl(resolveModificationFor(path),data);
     }
 
     private void mergeImpl(final OperationWithModification op,final NormalizedNode<?,?> data) {
-
-        if(data instanceof NormalizedNodeContainer<?,?,?>) {
+        if (data instanceof NormalizedNodeContainer<?,?,?>) {
             @SuppressWarnings({ "rawtypes", "unchecked" })
             NormalizedNodeContainer<?,?,NormalizedNode<PathArgument, ?>> dataContainer = (NormalizedNodeContainer) data;
             for(NormalizedNode<PathArgument, ?> child : dataContainer.getValue()) {
@@ -87,13 +87,14 @@ final class InMemoryDataTreeModification implements DataTreeModification {
     }
 
     @Override
-    public synchronized void delete(final YangInstanceIdentifier path) {
+    public void delete(final YangInstanceIdentifier path) {
         checkSealed();
+
         resolveModificationFor(path).delete();
     }
 
     @Override
-    public synchronized Optional<NormalizedNode<?, ?>> readNode(final YangInstanceIdentifier path) {
+    public Optional<NormalizedNode<?, ?>> readNode(final YangInstanceIdentifier path) {
         /*
          * Walk the tree from the top, looking for the first node between root and
          * the requested path which has been modified. If no such node exists,
@@ -115,7 +116,7 @@ final class InMemoryDataTreeModification implements DataTreeModification {
     private Optional<TreeNode> resolveSnapshot(final YangInstanceIdentifier path,
             final ModifiedNode modification) {
         final Optional<Optional<TreeNode>> potentialSnapshot = modification.getSnapshotCache();
-        if(potentialSnapshot.isPresent()) {
+        if (potentialSnapshot.isPresent()) {
             return potentialSnapshot.get();
         }
 
@@ -123,14 +124,14 @@ final class InMemoryDataTreeModification implements DataTreeModification {
             return resolveModificationStrategy(path).apply(modification, modification.getOriginal(),
                     version);
         } catch (Exception e) {
-            LOG.error("Could not create snapshot for {}:{}", path,modification,e);
+            LOG.error("Could not create snapshot for {}:{}", path, modification, e);
             throw e;
         }
     }
 
     private ModificationApplyOperation resolveModificationStrategy(final YangInstanceIdentifier path) {
         LOG.trace("Resolving modification apply strategy for {}", path);
-        if(rootNode.getType() == ModificationType.UNMODIFIED) {
+        if (rootNode.getType() == ModificationType.UNMODIFIED) {
             strategyTree.upgradeIfPossible();
         }
 
@@ -138,14 +139,17 @@ final class InMemoryDataTreeModification implements DataTreeModification {
     }
 
     private OperationWithModification resolveModificationFor(final YangInstanceIdentifier path) {
-        ModifiedNode modification = rootNode;
         // We ensure strategy is present.
-        ModificationApplyOperation operation = resolveModificationStrategy(path);
-        boolean isOrdered = true;
+        final ModificationApplyOperation operation = resolveModificationStrategy(path);
+
+        final boolean isOrdered;
         if (operation instanceof SchemaAwareApplyOperation) {
             isOrdered = ((SchemaAwareApplyOperation) operation).isOrdered();
+        } else {
+            isOrdered = true;
         }
 
+        ModifiedNode modification = rootNode;
         for (PathArgument pathArg : path.getPathArguments()) {
             modification = modification.modifyChild(pathArg, isOrdered);
         }
@@ -153,15 +157,15 @@ final class InMemoryDataTreeModification implements DataTreeModification {
     }
 
     @Override
-    public synchronized void ready() {
-        Preconditions.checkState(!sealed, "Attempted to seal an already-sealed Data Tree.");
-        sealed = true;
+    public void ready() {
+        final boolean wasRunning = UPDATER.compareAndSet(this, 0, 1);
+        Preconditions.checkState(wasRunning, "Attempted to seal an already-sealed Data Tree.");
+
         rootNode.seal();
     }
 
-    @GuardedBy("this")
     private void checkSealed() {
-        Preconditions.checkState(!sealed, "Data Tree is sealed. No further modifications allowed.");
+        Preconditions.checkState(sealed == 0, "Data Tree is sealed. No further modifications allowed.");
     }
 
     @Override
@@ -170,16 +174,17 @@ final class InMemoryDataTreeModification implements DataTreeModification {
     }
 
     @Override
-    public synchronized DataTreeModification newModification() {
-        Preconditions.checkState(sealed, "Attempted to chain on an unsealed modification");
+    public DataTreeModification newModification() {
+        Preconditions.checkState(sealed == 1, "Attempted to chain on an unsealed modification");
 
-        if(rootNode.getType() == ModificationType.UNMODIFIED) {
+        if (rootNode.getType() == ModificationType.UNMODIFIED) {
+            // Simple fast case: just use the underlying modification
             return snapshot.newModification();
         }
 
         /*
-         *  We will use preallocated version, this means returned snapshot will 
-         *  have same version each time this method is called.
+         * We will use preallocated version, this means returned snapshot will
+         * have same version each time this method is called.
          */
         TreeNode originalSnapshotRoot = snapshot.getRootNode();
         Optional<TreeNode> tempRoot = strategyTree.apply(rootNode, Optional.of(originalSnapshotRoot), version);
index 479a47f1069fb12d55d12ce8201b96f5c6b785fe..6832748bdaf49656883a864a016b7b60d670e1f2 100644 (file)
@@ -12,11 +12,14 @@ import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
 import com.google.common.base.Optional;
+import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 
 import java.io.ByteArrayOutputStream;
+import java.io.File;
 import java.net.URI;
 import java.util.AbstractMap;
+import java.util.Collections;
 import java.util.Map;
 
 import javax.xml.stream.XMLOutputFactory;
@@ -27,6 +30,13 @@ import org.custommonkey.xmlunit.XMLUnit;
 import org.junit.Test;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.api.SimpleNode;
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
+import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
 import org.w3c.dom.Document;
 
 public class XmlStreamUtilsTest {
@@ -70,6 +80,34 @@ public class XmlStreamUtilsTest {
         assertTrue("Xml differs: " + diff.toString(), identical);
     }
 
+    @Test
+    public void testEmptyNodeWithAttribute() throws Exception {
+        final ByteArrayOutputStream out = new ByteArrayOutputStream();
+        final XMLStreamWriter writer =  XML_OUTPUT_FACTORY.createXMLStreamWriter(out);
+
+        final Map<QName, String> attrs = Maps.newHashMap();
+        attrs.put(QName.create("namespaceAttr", "2012-12-12", "attr1"), "value");
+        final QName qName = QName.create("urn:opendaylight:controller:rpc:test", "2014-07-28", "cont");
+        final ImmutableCompositeNode dataAttributes = ImmutableCompositeNode.create(qName, attrs, Collections.<Node<?>>emptyList());
+        XmlStreamUtils.create(XmlUtils.DEFAULT_XML_CODEC_PROVIDER).writeDocument(writer, dataAttributes);
+
+        writer.close();
+        out.close();
+
+        final String xmlAsString = new String(out.toByteArray());
+
+        // TODO why resulting xml does not have namespace definition ? If sending xml by e.g. netconf the namespace is there but not here in test
+        final String expectedXmlAsString = "<cont xmlns:a=\"namespaceAttr\" a:attr1=\"value\"></cont>";
+
+        XMLUnit.setIgnoreAttributeOrder(true);
+        final Document control = XMLUnit.buildControlDocument(expectedXmlAsString);
+        final Document test = XMLUnit.buildTestDocument(xmlAsString);
+        final Diff diff = XMLUnit.compareXML(control, test);
+
+        final boolean identical = diff.identical();
+        assertTrue("Xml differs: " + diff.toString(), identical);
+    }
+
     private Map<String, String> mapPrefixed(final Iterable<Map.Entry<URI, String>> prefixes) {
         final Map<String, String> mappedPrefixes = Maps.newHashMap();
         for (final Map.Entry<URI, String> prefix : prefixes) {
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 56d240aed1ac426e4fd8ebd30ca4e87a9e09f446..b9f26f1b63a5986132cd7eef5da59553e6ad89dc 100644 (file)
@@ -12,12 +12,11 @@ import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
-
 import java.util.Arrays;
 import java.util.Iterator;
 import java.util.List;
 import java.util.NoSuchElementException;
-
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
 import org.opendaylight.yangtools.concepts.Immutable;
 import org.opendaylight.yangtools.yang.common.QName;
 
@@ -63,6 +62,10 @@ public abstract class SchemaPath implements Immutable {
         }
     }
 
+    @SuppressWarnings("rawtypes")
+    private static final AtomicReferenceFieldUpdater<SchemaPath, ImmutableList> LEGACYPATH_UPDATER =
+            AtomicReferenceFieldUpdater.newUpdater(SchemaPath.class, ImmutableList.class, "legacyPath");
+
     /**
      * Shared instance of the conceptual root schema node.
      */
@@ -97,13 +100,8 @@ public abstract class SchemaPath implements Immutable {
     private ImmutableList<QName> getLegacyPath() {
         ImmutableList<QName> ret = legacyPath;
         if (ret == null) {
-            synchronized (this) {
-                ret = legacyPath;
-                if (ret == null) {
-                    ret = ImmutableList.copyOf(getPathTowardsRoot()).reverse();
-                    legacyPath = ret;
-                }
-            }
+            ret = ImmutableList.copyOf(getPathTowardsRoot()).reverse();
+            LEGACYPATH_UPDATER.lazySet(this, ret);
         }
 
         return ret;
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 ad594e422116463671bb760fd75945ff647f0660..e63d7a8abcadf2615fdf04601d3c5f035f973d67 100644 (file)
@@ -7,14 +7,15 @@
 package org.opendaylight.yangtools.yang.model.repo.util;
 
 import com.google.common.annotations.Beta;
-import com.google.common.base.Preconditions;
+import com.google.common.base.FinalizablePhantomReference;
+import com.google.common.base.FinalizableReferenceQueue;
 import com.google.common.cache.Cache;
 import com.google.common.cache.CacheBuilder;
-import com.google.common.cache.RemovalListener;
-import com.google.common.cache.RemovalNotification;
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.Futures;
-
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
@@ -24,29 +25,14 @@ import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
 
 @Beta
-public class InMemorySchemaSourceCache<T extends SchemaSourceRepresentation> extends AbstractSchemaSourceCache<T> {
-    private static final class CacheEntry<T extends SchemaSourceRepresentation> {
-        private final SchemaSourceRegistration<T> reg;
-        private final T source;
-
-        public CacheEntry(final T source, final SchemaSourceRegistration<T> reg) {
-            this.source = Preconditions.checkNotNull(source);
-            this.reg = Preconditions.checkNotNull(reg);
-        }
-    }
-
-    private static final RemovalListener<SourceIdentifier, CacheEntry<?>> LISTENER = new RemovalListener<SourceIdentifier, CacheEntry<?>>() {
-        @Override
-        public void onRemoval(final RemovalNotification<SourceIdentifier, CacheEntry<?>> notification) {
-            notification.getValue().reg.close();
-        }
-    };
-
-    private final Cache<SourceIdentifier, CacheEntry<T>> cache;
+public class InMemorySchemaSourceCache<T extends SchemaSourceRepresentation> extends AbstractSchemaSourceCache<T> implements AutoCloseable {
+    private final List<FinalizablePhantomReference<T>> regs = Collections.synchronizedList(new ArrayList<FinalizablePhantomReference<T>>());
+    private final FinalizableReferenceQueue queue = new FinalizableReferenceQueue();
+    private final Cache<SourceIdentifier, T> cache;
 
     protected InMemorySchemaSourceCache(final SchemaSourceRegistry consumer, final Class<T> representation, final CacheBuilder<Object, Object> builder) {
         super(consumer, representation, Costs.IMMEDIATE);
-        cache = builder.removalListener(LISTENER).build();
+        cache = builder.build();
     }
 
     public static <R extends SchemaSourceRepresentation> InMemorySchemaSourceCache<R> createSoftCache(final SchemaSourceRegistry consumer, final Class<R> representation) {
@@ -55,9 +41,9 @@ public class InMemorySchemaSourceCache<T extends SchemaSourceRepresentation> ext
 
     @Override
     public CheckedFuture<? extends T, SchemaSourceException> getSource(final SourceIdentifier sourceIdentifier) {
-        final CacheEntry<T> present = cache.getIfPresent(sourceIdentifier);
+        final T present = cache.getIfPresent(sourceIdentifier);
         if (present != null) {
-            return Futures.immediateCheckedFuture(present.source);
+            return Futures.immediateCheckedFuture(present);
         }
 
         return Futures.<T, SchemaSourceException>immediateFailedCheckedFuture(new MissingSchemaSourceException("Source not found", sourceIdentifier));
@@ -65,10 +51,31 @@ public class InMemorySchemaSourceCache<T extends SchemaSourceRepresentation> ext
 
     @Override
     protected void offer(final T source) {
-        final CacheEntry<T> present = cache.getIfPresent(source.getIdentifier());
+        final T present = cache.getIfPresent(source.getIdentifier());
         if (present == null) {
+            cache.put(source.getIdentifier(), source);
+
             final SchemaSourceRegistration<T> reg = register(source.getIdentifier());
-            cache.put(source.getIdentifier(), new CacheEntry<T>(source, reg));
+            final FinalizablePhantomReference<T> ref = new FinalizablePhantomReference<T>(source, queue) {
+                @Override
+                public void finalizeReferent() {
+                    reg.close();
+                    regs.remove(this);
+                }
+            };
+
+            regs.add(ref);
         }
     }
+
+    @Override
+    public void close() {
+        while (!regs.isEmpty()) {
+            final FinalizablePhantomReference<?> ref = regs.get(0);
+            ref.finalizeReferent();
+        }
+
+        cache.invalidateAll();
+        queue.close();
+    }
 }
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
+}