Merge "BUG-1275: Expose XmlStreamUtils.writeValue()"
authorTony Tkacik <ttkacik@cisco.com>
Sun, 3 Aug 2014 18:17:16 +0000 (18:17 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Sun, 3 Aug 2014 18:17:16 +0000 (18:17 +0000)
160 files changed:
code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/BindingGeneratorImpl.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/ModuleContext.java
code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/TransformerGenerator.xtend
code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/util/BindingRuntimeContext.java [new file with mode: 0644]
code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/util/ClassLoaderUtils.java [deleted file]
code-generator/binding-generator-util/src/main/java/org/opendaylight/yangtools/binding/generator/util/generated/type/builder/AbstractGeneratedTypeBuilder.java
code-generator/binding-generator-util/src/main/java/org/opendaylight/yangtools/binding/generator/util/generated/type/builder/AbstractTypeMember.java
code-generator/binding-generator-util/src/main/java/org/opendaylight/yangtools/binding/generator/util/generated/type/builder/AbstractTypeMemberBuilder.java
code-generator/binding-generator-util/src/main/java/org/opendaylight/yangtools/binding/generator/util/generated/type/builder/AnnotationTypeBuilderImpl.java
code-generator/binding-generator-util/src/main/java/org/opendaylight/yangtools/binding/generator/util/generated/type/builder/EnumerationBuilderImpl.java
code-generator/binding-generator-util/src/main/java/org/opendaylight/yangtools/binding/generator/util/generated/type/builder/GeneratedTOBuilderImpl.java
code-generator/binding-generator-util/src/main/java/org/opendaylight/yangtools/binding/generator/util/generated/type/builder/MethodSignatureBuilderImpl.java
code-generator/binding-generator-util/src/main/java/org/opendaylight/yangtools/binding/generator/util/generated/type/builder/MethodSignatureImpl.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/main/java/org/opendaylight/yangtools/sal/java/api/generator/ClassTemplate.xtend
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/util/ClassLoaderUtils.java [deleted file]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/util/ListenerRegistry.java
common/features/pom.xml
common/parent/pom.xml
common/util/pom.xml
common/util/src/main/java/org/opendaylight/yangtools/util/ClassLoaderUtils.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/ExecutorServiceUtil.java
common/util/src/main/java/org/opendaylight/yangtools/util/Immutables.java [moved from common/concepts/src/main/java/org/opendaylight/yangtools/concepts/util/Immutables.java with 97% similarity]
common/util/src/main/java/org/opendaylight/yangtools/util/LazyCollections.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/ListenerRegistry.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/PropertyUtils.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/ReadOnlyTrieMap.java
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/AsyncNotifyingListenableFutureTask.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/AsyncNotifyingListeningExecutorService.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/CachedThreadPoolExecutor.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/DeadlockDetectingListeningExecutorService.java
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/ExceptionMapper.java
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/FastThreadPoolExecutor.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/NotificationManager.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/QueuedNotificationManager.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/ReflectiveExceptionMapper.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/SpecialExecutors.java [new file with mode: 0644]
common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/AsyncNotifyingListeningExecutorServiceTest.java [new file with mode: 0644]
common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/CommonTestUtils.java [new file with mode: 0644]
common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/DeadlockDetectingListeningExecutorServiceTest.java
common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/QueuedNotificationManagerTest.java [new file with mode: 0644]
common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/ReflectiveExceptionMapperTest.java [new file with mode: 0644]
common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/ThreadPoolExecutorTest.java [new file with mode: 0644]
yang/pom.xml
yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/BindingMapping.java
yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/BindingStreamEventWriter.java [new file with mode: 0644]
yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/DataObjectSerializer.java [new file with mode: 0644]
yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/DataObjectSerializerImplementation.java [new file with mode: 0644]
yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/DataObjectSerializerRegistry.java [new file with mode: 0644]
yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/AugmentationFieldGetter.java [new file with mode: 0644]
yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/BindingReflections.java
yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/ClassLoaderUtils.java
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/NormalizedNodeStreamWriter.java [new file with mode: 0644]
yang/yang-data-composite-node/pom.xml [moved from yang/yang-data-json/pom.xml with 76% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/parser/AnyXmlNodeCnSnParser.java [new file with mode: 0644]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/parser/AugmentationNodeCnSnParser.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/parser/AugmentationNodeCnSnParser.java with 88% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/parser/ChoiceNodeCnSnParser.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/parser/ChoiceNodeCnSnParser.java with 87% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/parser/CnSnToNormalizedNodeParserFactory.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/parser/CnSnToNormalizedNodeParserFactory.java with 90% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/parser/ContainerNodeCnSnParser.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/parser/ContainerNodeCnSnParser.java with 89% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/parser/LeafNodeCnSnParser.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/parser/LeafNodeCnSnParser.java with 92% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/parser/LeafSetEntryNodeCnSnParser.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/parser/LeafSetEntryNodeCnSnParser.java with 93% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/parser/LeafSetNodeCnSnParser.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/parser/LeafSetNodeCnSnParser.java with 93% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/parser/MapEntryNodeCnSnParser.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/parser/MapEntryNodeCnSnParser.java with 89% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/parser/MapNodeCnSnParser.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/parser/MapNodeCnSnParser.java with 93% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/serializer/AnyXmlNodeCnSnSerializer.java [new file with mode: 0644]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/serializer/AugmentationNodeCnSnSerializer.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/serializer/AugmentationNodeCnSnSerializer.java with 92% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/serializer/ChoiceNodeCnSnSerializer.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/serializer/ChoiceNodeCnSnSerializer.java with 92% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/serializer/CnSnFromNormalizedNodeSerializerFactory.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/serializer/CnSnFromNormalizedNodeSerializerFactory.java with 89% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/serializer/ContainerNodeCnSnSerializer.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/serializer/ContainerNodeCnSnSerializer.java with 62% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/serializer/LeafNodeCnSnSerializer.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/serializer/LeafNodeCnSnSerializer.java with 91% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/serializer/LeafSetEntryNodeCnSnSerializer.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/serializer/LeafSetEntryNodeCnSnSerializer.java with 92% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/serializer/LeafSetNodeCnSnSerializer.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/serializer/LeafSetNodeCnSnSerializer.java with 93% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/serializer/MapEntryNodeCnSnSerializer.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/serializer/MapEntryNodeCnSnSerializer.java with 60% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/serializer/MapNodeCnSnSerializer.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/serializer/MapNodeCnSnSerializer.java with 93% similarity]
yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/json/CnSnToNormalizedNodesUtils.java [moved from yang/yang-data-json/src/main/java/org/opendaylight/yangtools/yang/data/json/schema/json/CnSnToNormalizedNodesUtils.java with 93% similarity]
yang/yang-data-composite-node/src/test/java/org/opendaylight/yangtools/yang/data/composite/node/schema/TestUtils.java [moved from yang/yang-data-json/src/test/java/org/opendaylight/yangtools/yang/data/json/schema/TestUtils.java with 69% similarity]
yang/yang-data-composite-node/src/test/java/org/opendaylight/yangtools/yang/data/composite/node/schema/parser/ParseCnSnStructToNormalizedStructTest.java [moved from yang/yang-data-json/src/test/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/parser/ParseCnSnStructToNormalizedStructTest.java with 87% similarity]
yang/yang-data-composite-node/src/test/java/org/opendaylight/yangtools/yang/data/composite/node/schema/serializer/SerializeNormalizedStructToCnSnStructTest.java [moved from yang/yang-data-json/src/test/java/org/opendaylight/yangtools/yang/data/json/schema/cnsn/serializer/SerializeNormalizedStructToCnSnStructTest.java with 88% similarity]
yang/yang-data-composite-node/src/test/resources/cnsn-to-normalized-node/json/simple-container.json [moved from yang/yang-data-json/src/test/resources/cnsn-to-normalized-node/json/simple-container.json with 82% similarity]
yang/yang-data-composite-node/src/test/resources/cnsn-to-normalized-node/yang/augment-simple-container.yang [moved from yang/yang-data-json/src/test/resources/cnsn-to-normalized-node/yang/augment-simple-container.yang with 100% similarity]
yang/yang-data-composite-node/src/test/resources/cnsn-to-normalized-node/yang/simple-container.yang [moved from yang/yang-data-json/src/test/resources/cnsn-to-normalized-node/yang/simple-container.yang with 92% similarity]
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/schema/ImmutableNodes.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/ImmutableNormalizedNodeStreamWriter.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/NormalizedNodeResult.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/transform/FromNormalizedNodeSerializerFactory.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/transform/ToNormalizedNodeParserFactory.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/transform/base/parser/AnyXmlNodeBaseParser.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/transform/base/parser/NodeParserDispatcher.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/transform/base/serializer/AnyXmlNodeBaseSerializer.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/transform/base/serializer/NodeSerializerDispatcher.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/transform/dom/parser/DomToNormalizedNodeParserFactory.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/transform/dom/serializer/DomFromNormalizedNodeSerializerFactory.java
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/test/RpcReplyToDomTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/schema/rpc-test-model.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/schema/rpc-test-payload1.xml [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/schema/rpc-test-payload2.xml [new file with mode: 0644]
yang/yang-maven-plugin-it/src/test/resources/test-parent/AdditionalConfig/pom.xml
yang/yang-maven-plugin-it/src/test/resources/test-parent/Correct/pom.xml
yang/yang-maven-plugin-it/src/test/resources/test-parent/GenerateTest1/pom.xml
yang/yang-maven-plugin-it/src/test/resources/test-parent/GenerateTest2/pom.xml
yang/yang-maven-plugin-it/src/test/resources/test-parent/Generator/pom.xml
yang/yang-maven-plugin-it/src/test/resources/test-parent/InvalidVersion/pom.xml
yang/yang-maven-plugin-it/src/test/resources/test-parent/MissingYangInDep/pom.xml
yang/yang-maven-plugin-it/src/test/resources/test-parent/NamingConflict/pom.xml
yang/yang-maven-plugin-it/src/test/resources/test-parent/NoGenerators/pom.xml
yang/yang-maven-plugin-it/src/test/resources/test-parent/NoOutputDir/pom.xml
yang/yang-maven-plugin-it/src/test/resources/test-parent/NoYangFiles/pom.xml
yang/yang-maven-plugin-it/src/test/resources/test-parent/UnknownGenerator/pom.xml
yang/yang-maven-plugin-it/src/test/resources/test-parent/YangRootNotExist/pom.xml
yang/yang-maven-plugin-it/src/test/resources/test-parent/pom.xml
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/AcceptingSchemaSourceFilter.java [deleted file]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/MissingSchemaSourceException.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaContextFactory.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaRepository.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaResolutionException.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaSourceException.java [moved from yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceTransformationException.java with 53% similarity]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaSourceFilter.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaSourceRepresentation.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/api/YangTextSchemaSource.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YinSchemaSource.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/PotentialSchemaSource.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaListenerRegistration.java [moved from yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaTransformerRegistration.java with 58% similarity]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceListener.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceProvider.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceRegistration.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceRegistry.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceTransformer.java [deleted file]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaListenerRegistration.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaRepository.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaSourceCache.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaSourceRegistration.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/InMemorySchemaSourceCache.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/RefcountedRegistration.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/SchemaSourceTransformer.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/BaseTypes.java
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/InstanceIdentifierType.java [moved from yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/InstanceIdentifier.java with 87% similarity]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/package-info.java
yang/yang-parser-impl/src/main/antlr/YangLexer.g4
yang/yang-parser-impl/src/main/antlr/YangParser.g4
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/ParserListenerUtils.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangModelBasicValidationListener.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangParserImpl.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/impl/util/YangModelDependencyInfo.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/AbstractURLRegistration.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/DependencyResolver.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaContextFactory.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaRepository.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/URLRegistration.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/URLSchemaContextResolver.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/util/ASTSchemaSource.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/util/TextToASTTransformer.java [new file with mode: 0644]
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/parser/impl/GroupingTest.java
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/parser/impl/TypesResolutionTest.java
yang/yang-parser-impl/src/test/resources/model/foo.yang
yang/yang-parser-impl/src/test/resources/types/union-with-ext/extdef.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/types/union-with-ext/unionbug.yang [new file with mode: 0644]

index d448db765947b544e5d2ef3d3da289ab52da4af0..59ea3c53f3ce426324d06e92e924da5a3e4484d3 100644 (file)
@@ -285,7 +285,7 @@ public class BindingGeneratorImpl implements BindingGenerator {
         final GeneratedTypeBuilder genType = addDefaultInterfaceDefinition(packageName, node, childOf);
         genType.addComment(node.getDescription());
         if (node instanceof DataNodeContainer) {
-            genCtx.get(module).addChildNodeType(node.getPath(), genType);
+            genCtx.get(module).addChildNodeType(node, genType);
             groupingsToGenTypes(module, ((DataNodeContainer) node).getGroupings());
             processUsesAugments((DataNodeContainer) node, module);
         }
@@ -459,7 +459,7 @@ public class BindingGeneratorImpl implements BindingGenerator {
                     inType.addImplementsType(DATA_OBJECT);
                     inType.addImplementsType(augmentable(inType));
                     resolveDataSchemaNodes(module, basePackageName, inType, inType, input.getChildNodes());
-                    genCtx.get(module).addChildNodeType(input.getPath(), inType);
+                    genCtx.get(module).addChildNodeType(input, inType);
                     final GeneratedType inTypeInstance = inType.toInstance();
                     method.addParameter(inTypeInstance, "input");
                 }
@@ -471,7 +471,7 @@ public class BindingGeneratorImpl implements BindingGenerator {
                     outType.addImplementsType(DATA_OBJECT);
                     outType.addImplementsType(augmentable(outType));
                     resolveDataSchemaNodes(module, basePackageName, outType, outType, output.getChildNodes());
-                    genCtx.get(module).addChildNodeType(output.getPath(), outType);
+                    genCtx.get(module).addChildNodeType(output, outType);
                     outTypeInstance = outType.toInstance();
                 }
 
@@ -520,7 +520,7 @@ public class BindingGeneratorImpl implements BindingGenerator {
                 final GeneratedTypeBuilder notificationInterface = addDefaultInterfaceDefinition(basePackageName,
                         notification, BindingTypes.DATA_OBJECT);
                 notificationInterface.addImplementsType(NOTIFICATION);
-                genCtx.get(module).addChildNodeType(notification.getPath(), notificationInterface);
+                genCtx.get(module).addChildNodeType(notification, notificationInterface);
 
                 // Notification object
                 resolveDataSchemaNodes(module, basePackageName, notificationInterface, notificationInterface,
@@ -1116,7 +1116,7 @@ public class BindingGeneratorImpl implements BindingGenerator {
             constructGetter(parent, choiceNode.getQName().getLocalName(), choiceNode.getDescription(),
                     choiceTypeBuilder);
             choiceTypeBuilder.addImplementsType(typeForClass(DataContainer.class));
-            genCtx.get(module).addChildNodeType(choiceNode.getPath(), choiceTypeBuilder);
+            genCtx.get(module).addChildNodeType(choiceNode, choiceTypeBuilder);
             generateTypesFromChoiceCases(module, basePackageName, choiceTypeBuilder.toInstance(), choiceNode);
         }
     }
index ee5d244f9df735f032c48240d860bab1920d2161..b6b3e6d5e99e9cbb6e287a9209cd6d1fa932870e 100644 (file)
@@ -9,18 +9,13 @@ package org.opendaylight.yangtools.sal.binding.generator.impl;
 
 import com.google.common.base.Optional;
 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 com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
 import com.google.common.collect.HashMultimap;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Multimap;
 import com.google.common.collect.Multimaps;
-
 import java.lang.ref.WeakReference;
-import java.lang.reflect.Field;
 import java.util.AbstractMap.SimpleEntry;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -35,7 +30,6 @@ import java.util.Set;
 import java.util.WeakHashMap;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
-
 import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl;
 import org.opendaylight.yangtools.binding.generator.util.Types;
 import org.opendaylight.yangtools.concepts.Delegator;
@@ -55,6 +49,7 @@ import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.Identifier;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.binding.util.ClassLoaderUtils;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.data.api.Node;
@@ -133,8 +128,7 @@ class LazyGeneratedCodecRegistry implements CodecRegistry, SchemaContextListener
     private final AbstractTransformerGenerator generator;
     private final SchemaLock lock;
 
-    private static final LoadingCache<Class<?>, AugmentationFieldGetter> AUGMENTATION_GETTERS =
-            CacheBuilder.newBuilder().weakKeys().softValues().build(new AugmentationGetterLoader());
+
 
     // FIXME: how is this protected?
     private SchemaContext currentSchema;
@@ -350,18 +344,36 @@ class LazyGeneratedCodecRegistry implements CodecRegistry, SchemaContextListener
         }
     }
 
+    @SuppressWarnings("unchecked")
+    @Override
+    public IdentifierCodec<?> getIdentifierCodecForIdentifiable(final Class identifiable) {
+
+        Class identifier= ClassLoaderUtils.findFirstGenericArgument(identifiable, org.opendaylight.yangtools.yang.binding.Identifiable.class);
+        IdentifierCodec<?> obj = identifierCodecs.get(identifier);
+        if (obj != null) {
+            return obj;
+        }
+        return createIdentifierCodec(identifier,identifiable);
+    }
+
     @Override
-    public <T extends Identifiable<?>> IdentifierCodec<?> getIdentifierCodecForIdentifiable(final Class<T> type) {
-        IdentifierCodec<?> obj = identifierCodecs.get(type);
+    public <T extends Identifier<?>> IdentifierCodec<T> getCodecForIdentifier(final Class<T> identifier) {
+        @SuppressWarnings("unchecked")
+        IdentifierCodec<T> obj = (IdentifierCodec<T>) identifierCodecs.get(identifier);
         if (obj != null) {
             return obj;
         }
+        Class<? extends Identifiable<T>> identifiable = ClassLoaderUtils.findFirstGenericArgument(identifier, Identifier.class);
+        return createIdentifierCodec(identifier,identifiable);
+    }
+
+    private <T extends Identifier<?>> IdentifierCodec<T> createIdentifierCodec(final Class<T> identifier,final Class<? extends Identifiable<T>> identifiable){
         Class<? extends BindingCodec<Map<QName, Object>, Object>> newCodec = generator
-                .keyTransformerForIdentifiable(type);
+                .keyTransformerForIdentifiable(identifiable);
         BindingCodec<Map<QName, Object>, Object> newInstance;
         newInstance = newInstanceOf(newCodec);
-        IdentifierCodecImpl<?> newWrapper = new IdentifierCodecImpl<>(newInstance);
-        identifierCodecs.put(type, newWrapper);
+        IdentifierCodecImpl<T> newWrapper = new IdentifierCodecImpl<>(newInstance);
+        identifierCodecs.put(identifier, newWrapper);
         return newWrapper;
     }
 
@@ -383,22 +395,6 @@ class LazyGeneratedCodecRegistry implements CodecRegistry, SchemaContextListener
         CodecMapping.setIdentityRefCodec(cls, identityRefCodec);
     }
 
-    @Override
-    public <T extends Identifier<?>> IdentifierCodec<T> getCodecForIdentifier(final Class<T> object) {
-        @SuppressWarnings("unchecked")
-        IdentifierCodec<T> obj = (IdentifierCodec<T>) identifierCodecs.get(object);
-        if (obj != null) {
-            return obj;
-        }
-        Class<? extends BindingCodec<Map<QName, Object>, Object>> newCodec = generator
-                .keyTransformerForIdentifier(object);
-        BindingCodec<Map<QName, Object>, Object> newInstance;
-        newInstance = newInstanceOf(newCodec);
-        IdentifierCodecImpl<T> newWrapper = new IdentifierCodecImpl<>(newInstance);
-        identifierCodecs.put(object, newWrapper);
-        return newWrapper;
-    }
-
     @SuppressWarnings("rawtypes")
     public ChoiceCaseCodecImpl getCaseCodecFor(final Class caseClass) {
         ChoiceCaseCodecImpl<?> potential = caseCodecs.get(caseClass);
@@ -520,50 +516,7 @@ class LazyGeneratedCodecRegistry implements CodecRegistry, SchemaContextListener
         return ret;
     }
 
-    private static final class AugmentationGetterLoader extends CacheLoader<Class<?>, AugmentationFieldGetter> {
-        private static final AugmentationFieldGetter DUMMY = new AugmentationFieldGetter() {
-            @Override
-            Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentations(final Object input) {
-                return Collections.emptyMap();
-            }
-        };
-
-        @Override
-        public AugmentationFieldGetter load(final Class<?> key) throws Exception {
-            Field field;
-            try {
-                field = key.getDeclaredField("augmentation");
-            } catch (NoSuchFieldException | SecurityException e) {
-                LOG.debug("Failed to acquire augmentation field", e);
-                return DUMMY;
-            }
-            field.setAccessible(true);
-
-            return new ReflectionAugmentationFieldGetter(field);
-        }
-    }
-
-    private static abstract class AugmentationFieldGetter {
-        abstract Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentations(final Object input);
-    }
-
-    private static final class ReflectionAugmentationFieldGetter extends AugmentationFieldGetter {
-        private final Field augmentationField;
-
-        ReflectionAugmentationFieldGetter(final Field augmentationField) {
-            this.augmentationField = Preconditions.checkNotNull(augmentationField);
-        }
 
-        @Override
-        @SuppressWarnings("unchecked")
-        Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentations(final Object input) {
-            try {
-                return (Map<Class<? extends Augmentation<?>>, Augmentation<?>>) augmentationField.get(input);
-            } catch (IllegalArgumentException | IllegalAccessException e) {
-                throw new IllegalStateException("Failed to access augmentation field", e);
-            }
-        }
-    }
 
     private static abstract class IntermediateCodec<T> implements DomCodec<T>, Delegator<BindingCodec<Map<QName, Object>, Object>> {
 
@@ -1119,24 +1072,13 @@ class LazyGeneratedCodecRegistry implements CodecRegistry, SchemaContextListener
         public Object serialize(final Object input) {
             Preconditions.checkArgument(augmentableType.isInstance(input), "Object %s is not instance of %s ",input,augmentableType);
             if (input instanceof Augmentable<?>) {
-                Map<Class<? extends Augmentation<?>>, Augmentation<?>> augmentations = getAugmentations(input);
+                Map<Class<? extends Augmentation<?>>, Augmentation<?>> augmentations = BindingReflections.getAugmentations((Augmentable<?>) input);
                 return serializeImpl(augmentations);
             }
             return null;
         }
 
-        /**
-         *
-         * Extracts augmentation from Binding DTO field using reflection
-         *
-         * @param input Instance of DataObject which is augmentable and
-         *      may contain augmentation
-         * @return Map of augmentations if read was successful, otherwise
-         *      empty map.
-         */
-        private Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentations(final Object input) {
-            return AUGMENTATION_GETTERS.getUnchecked(input.getClass()).getAugmentations(input);
-        }
+
 
         /**
          *
@@ -1493,4 +1435,5 @@ class LazyGeneratedCodecRegistry implements CodecRegistry, SchemaContextListener
     private static final Type referencedType(final Class<?> augmentableType) {
         return new ReferencedTypeImpl(augmentableType.getPackage().getName(), augmentableType.getSimpleName());
     }
+
 }
index acb939d14962f0df818af2950bb1e8b5a1c35ede..57fc8d306f524167be62455327ad27ccb8491c28 100644 (file)
@@ -10,8 +10,11 @@ package org.opendaylight.yangtools.sal.binding.generator.impl;
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
 import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Maps;
 import com.google.common.collect.Multimap;
+import com.google.common.collect.Multimaps;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -23,6 +26,8 @@ import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTy
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 
 public final class ModuleContext {
@@ -37,6 +42,9 @@ public final class ModuleContext {
     private final List<GeneratedTypeBuilder> augmentations = new ArrayList<GeneratedTypeBuilder>();
     private final BiMap<Type,AugmentationSchema> typeToAugmentation = HashBiMap.create();
 
+    private final Map<Type,Object> typeToSchema = new HashMap<>();
+
+
     private final Multimap<Type, Type> choiceToCases = HashMultimap.create();
     private final BiMap<Type,ChoiceCaseNode> caseTypeToSchema = HashBiMap.create();
 
@@ -79,11 +87,11 @@ public final class ModuleContext {
     }
 
     public Multimap<Type, Type> getChoiceToCases() {
-        return choiceToCases;
+        return Multimaps.unmodifiableMultimap(choiceToCases);
     }
 
     public Multimap<Type, Type> getAugmentableToAugmentations() {
-        return augmentableToAugmentations;
+        return Multimaps.unmodifiableMultimap(augmentableToAugmentations);
     }
 
     public GeneratedTypeBuilder getModuleNode() {
@@ -110,8 +118,9 @@ public final class ModuleContext {
         genTOs.add(b);
     }
 
-    public void addChildNodeType(final SchemaPath p, final GeneratedTypeBuilder b) {
-        childNodes.put(p, b);
+    public void addChildNodeType(final SchemaNode p, final GeneratedTypeBuilder b) {
+        childNodes.put(p.getPath(), b);
+        typeToSchema.put(b,p);
     }
 
     public void addGroupingType(final SchemaPath p, final GeneratedTypeBuilder b) {
@@ -143,48 +152,62 @@ public final class ModuleContext {
     }
 
     public Map<SchemaPath, GeneratedTypeBuilder> getChildNodes() {
-        return childNodes;
+        return Collections.unmodifiableMap(childNodes);
     }
 
     public Map<SchemaPath, GeneratedTypeBuilder> getGroupings() {
-        return groupings;
+        return Collections.unmodifiableMap(groupings);
     }
 
     public Map<SchemaPath, GeneratedTypeBuilder> getCases() {
-        return cases;
+        return Collections.unmodifiableMap(cases);
     }
 
     public Map<QName,GeneratedTOBuilder> getIdentities() {
-        return identities;
+        return Collections.unmodifiableMap(identities);
     }
 
     public Set<GeneratedTypeBuilder> getTopLevelNodes() {
-        return topLevelNodes;
+        return Collections.unmodifiableSet(topLevelNodes);
     }
 
     public List<GeneratedTypeBuilder> getAugmentations() {
-        return augmentations;
+        return Collections.unmodifiableList(augmentations);
     }
 
     public BiMap<Type, AugmentationSchema> getTypeToAugmentation() {
-        return typeToAugmentation;
+        return Maps.unmodifiableBiMap(typeToAugmentation);
     }
 
     public void addTypeToAugmentation(final GeneratedTypeBuilder builder, final AugmentationSchema schema) {
         typeToAugmentation.put(builder, schema);
+        typeToSchema.put(builder, schema);
     }
 
     public void addTargetToAugmentation(final Type target, final GeneratedTypeBuilder augmentation) {
         augmentableToAugmentations.put(target,augmentation);
     }
 
-    public void addChoiceToCaseMapping(Type choiceType, Type caseType, ChoiceCaseNode schema) {
+    public void addChoiceToCaseMapping(final Type choiceType, final Type caseType, final ChoiceCaseNode schema) {
         choiceToCases.put(choiceType, caseType);
         caseTypeToSchema.put(caseType, schema);
+        typeToSchema.put(caseType, schema);
     }
 
     public BiMap<Type, ChoiceCaseNode> getCaseTypeToSchemas() {
-        return caseTypeToSchema;
+        return Maps.unmodifiableBiMap(caseTypeToSchema);
+    }
+
+    /**
+     *
+     * Returns mapping of type to its schema.
+     *
+     * Valid values are only instances of {@link DataSchemaNode} or {@link AugmentationSchema}
+     *
+     * @return
+     */
+    public Map<Type, Object> getTypeToSchema() {
+        return Collections.unmodifiableMap(typeToSchema);
     }
 
 }
index ab6b734c0bb540fdefb7bd5a94b1fa98c0cee7b8..769a9e5bb77c054510fdca274afbeb3d44a91d12 100644 (file)
@@ -29,7 +29,6 @@ import javassist.CtMethod
 import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil
 import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl
 import org.opendaylight.yangtools.binding.generator.util.Types
-import org.opendaylight.yangtools.sal.binding.generator.util.ClassLoaderUtils
 import org.opendaylight.yangtools.sal.binding.generator.util.CodeGenerationException
 import org.opendaylight.yangtools.sal.binding.generator.util.XtendHelper
 import org.opendaylight.yangtools.sal.binding.model.api.Enumeration
@@ -39,6 +38,7 @@ import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType
 import org.opendaylight.yangtools.sal.binding.model.api.ParameterizedType
 import org.opendaylight.yangtools.sal.binding.model.api.Type
 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTypeBuilder
+import org.opendaylight.yangtools.util.ClassLoaderUtils
 import org.opendaylight.yangtools.yang.binding.Augmentation
 import org.opendaylight.yangtools.yang.binding.BindingCodec
 import org.opendaylight.yangtools.yang.binding.BindingDeserializer
diff --git a/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/util/BindingRuntimeContext.java b/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/util/BindingRuntimeContext.java
new file mode 100644 (file)
index 0000000..c5fb378
--- /dev/null
@@ -0,0 +1,287 @@
+package org.opendaylight.yangtools.sal.binding.generator.util;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Multimap;
+import java.util.AbstractMap;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.sal.binding.generator.api.ClassLoadingStrategy;
+import org.opendaylight.yangtools.sal.binding.generator.impl.BindingGeneratorImpl;
+import org.opendaylight.yangtools.sal.binding.generator.impl.BindingSchemaContextUtils;
+import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleContext;
+import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType;
+import org.opendaylight.yangtools.sal.binding.model.api.MethodSignature;
+import org.opendaylight.yangtools.sal.binding.model.api.ParameterizedType;
+import org.opendaylight.yangtools.sal.binding.model.api.Type;
+import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTypeBuilder;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.AugmentationSchemaProxy;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.util.SchemaNodeUtils;
+/**
+ *
+ * Runtime Context for Java YANG Binding classes
+ *
+ *<p>
+ * Runtime Context provides additional insight in Java YANG Binding,
+ * binding classes and underlying YANG schema, it contains
+ * runtime information, which could not be derived from generated
+ * classes alone using {@link org.opendaylight.yangtools.yang.binding.util.BindingReflections}.
+ * <p>
+ * Some of this information are for example list of all available
+ * children for cases {@link #getChoiceCaseChildren(DataNodeContainer)}, since
+ * choices are augmentable and new choices may be introduced by additional models.
+ * <p>
+ * Same goes for all possible augmentations.
+ *
+ */
+public class BindingRuntimeContext implements Immutable {
+
+    private final ClassLoadingStrategy strategy;
+    private final SchemaContext schemaContext;
+
+    private final Map<Type, AugmentationSchema> augmentationToSchema = new HashMap<>();
+    private final BiMap<Type, Object> typeToDefiningSchema = HashBiMap.create();
+    private final Multimap<Type, Type> augmentableToAugmentations = HashMultimap.create();
+    private final Multimap<Type, Type> choiceToCases = HashMultimap.create();
+
+    private BindingRuntimeContext(final ClassLoadingStrategy strategy, final SchemaContext schema) {
+        this.strategy = strategy;
+        this.schemaContext = schema;
+
+        BindingGeneratorImpl generator = new BindingGeneratorImpl();
+        generator.generateTypes(schema);
+        Map<Module, ModuleContext> modules = generator.getModuleContexts();
+
+        for (Entry<Module, ModuleContext> entry : modules.entrySet()) {
+            ModuleContext ctx = entry.getValue();
+            augmentationToSchema.putAll(ctx.getTypeToAugmentation());
+            typeToDefiningSchema.putAll(ctx.getTypeToSchema());
+            augmentableToAugmentations.putAll(ctx.getAugmentableToAugmentations());
+            choiceToCases.putAll(ctx.getChoiceToCases());
+        }
+    }
+
+    /**
+     *
+     * Creates Binding Runtime Context from supplied class loading strategy and schema context.
+     *
+     * @param strategy Class loading strategy to retrieve generated Binding classes
+     * @param ctx Schema Context which describes YANG model and to which Binding classes should be mapped
+     * @return Instance of BindingRuntimeContext for supplied schema context.
+     */
+    public static final BindingRuntimeContext create(final ClassLoadingStrategy strategy, final SchemaContext ctx) {
+
+        return new BindingRuntimeContext(strategy, ctx);
+    }
+
+    /**
+     * Returns a class loading strategy associated with this binding runtime context
+     * which is used to load classes.
+     *
+     * @return Class loading strategy.
+     */
+    public ClassLoadingStrategy getStrategy() {
+        return strategy;
+    }
+
+    /**
+     * Returns an stable immutable view of schema context associated with this Binding runtime context.
+     *
+     * @return stable view of schema context
+     */
+    public SchemaContext getSchemaContext() {
+        return schemaContext;
+    }
+
+    /**
+     * Returns schema of augmentation
+     * <p>
+     * Returned schema is schema definition from which augmentation class was generated.
+     * This schema is isolated from other augmentations. This means it contains
+     * augmentation definition as was present in original YANG module.
+     * <p>
+     * Children of returned schema does not contain any additional augmentations,
+     * which may be present in runtime for them, thus returned schema is unsuitable
+     * for use for validation of data.
+     * <p>
+     * For retrieving {@link AugmentationSchema}, which will contains
+     * full model for child nodes, you should use method {@link #getResolvedAugmentationSchema(DataNodeContainer, Class)}
+     * which will return augmentation schema derived from supplied augmentation target
+     * schema.
+     *
+     * @param augClass Augmentation class
+     * @return Schema of augmentation
+     * @throws IllegalArgumentException If supplied class is not an augmentation or current context does not contain schema for augmenation.
+     */
+    public AugmentationSchema getAugmentationDefinition(final Class<?> augClass) throws IllegalArgumentException {
+        Preconditions.checkArgument(Augmentation.class.isAssignableFrom(augClass),"Class {} does not represent augmentation",augClass);
+        final AugmentationSchema ret = augmentationToSchema.get(referencedType(augClass));
+        Preconditions.checkArgument(ret != null, "Supplied augmentation {} is not valid in current context",augClass);
+        return ret;
+    }
+
+    /**
+     * Returns defining {@link DataSchemaNode} for supplied class.
+     *
+     * <p>
+     * Returned schema is schema definition from which class was generated.
+     * This schema may be isolated from augmentations, if supplied class
+     * represent node, which was child of grouping or augmentation.
+     * <p>
+     * For getting augmentation schema from augmentation class use
+     * {@link #getAugmentationDefinition(Class)} instead.
+     *
+     * @param cls Class which represents list, container, choice or case.
+     * @return Schema node, from which class was generated.
+     */
+    public DataSchemaNode getSchemaDefinition(final Class<?> cls) {
+        Preconditions.checkArgument(Augmentation.class.isAssignableFrom(cls));
+        return (DataSchemaNode) typeToDefiningSchema.get(referencedType(cls));
+    }
+
+    public Entry<AugmentationIdentifier, AugmentationSchema> getResolvedAugmentationSchema(final DataNodeContainer target,
+            final Class<? extends Augmentation<?>> aug) {
+        AugmentationSchema origSchema = getAugmentationDefinition(aug);
+        /*
+         * FIXME: Validate augmentation schema lookup
+         *
+         * Currently this algorithm, does not verify if instantiated child nodes
+         * are real one derived from augmentation schema. The problem with
+         * full validation is, if user used copy builders, he may use
+         * augmentation which was generated for different place.
+         *
+         * If this augmentations have same definition, we emit same identifier
+         * with data and it is up to underlying user to validate data.
+         *
+         */
+        Set<QName> childNames = new HashSet<>();
+        Set<DataSchemaNode> realChilds = new HashSet<>();
+        for (DataSchemaNode child : origSchema.getChildNodes()) {
+            realChilds.add(target.getDataChildByName(child.getQName()));
+            childNames.add(child.getQName());
+        }
+
+        AugmentationIdentifier identifier = new AugmentationIdentifier(childNames);
+        AugmentationSchema proxy = new AugmentationSchemaProxy(origSchema, realChilds);
+        return new AbstractMap.SimpleEntry<>(identifier, proxy);
+    }
+
+    /**
+     *
+     * Returns resolved case schema for supplied class
+     *
+     * @param schema Resolved parent choice schema
+     * @param childClass Class representing case.
+     * @return Resolved case schema.
+     * @throws IllegalArgumentException If supplied class does not represent case or supplied case class is not
+     * valid in the context of parent choice schema.
+     */
+    public ChoiceCaseNode getCaseSchemaDefinition(final ChoiceNode schema, final Class<?> childClass) throws IllegalArgumentException {
+        DataSchemaNode origSchema = getSchemaDefinition(childClass);
+        Preconditions.checkArgument(origSchema instanceof ChoiceCaseNode, "Supplied {} is not case.");
+        /* FIXME: Make sure that if there are multiple augmentations of same
+         * named case, with same structure we treat it as equals
+         * this is due property of Binding specification and copy builders
+         * that user may be unaware that he is using incorrect case
+         * which was generated for choice inside grouping.
+         */
+        Optional<ChoiceCaseNode> found = BindingSchemaContextUtils.findInstantiatedCase(schema,
+                (ChoiceCaseNode) origSchema);
+        Preconditions.checkArgument(found.isPresent(), "Supplied {} is not valid case in schema", schema);
+        return found.get();
+    }
+
+    private static Type referencedType(final Class<?> type) {
+        return new ReferencedTypeImpl(type.getPackage().getName(), type.getSimpleName());
+    }
+
+    public Entry<GeneratedType, Object> getTypeWithSchema(final Class<?> type) {
+        Object schema = typeToDefiningSchema.get(referencedType(type));
+        Type definedType = typeToDefiningSchema.inverse().get(schema);
+        Preconditions.checkNotNull(schema);
+        Preconditions.checkNotNull(definedType);
+
+        return new SimpleEntry<>(((GeneratedTypeBuilder) definedType).toInstance(), schema);
+    }
+
+    public ImmutableMap<Type, Entry<Type, Type>> getChoiceCaseChildren(final DataNodeContainer schema) {
+        Map<Type,Entry<Type,Type>> childToCase = new HashMap<>();;
+        for(ChoiceNode choice :  FluentIterable.from(schema.getChildNodes()).filter(ChoiceNode.class)) {
+            ChoiceNode originalChoice = getOriginalSchema(choice);
+            Type choiceType = referencedType(typeToDefiningSchema.inverse().get(originalChoice));
+            Collection<Type> cases = choiceToCases.get(choiceType);
+
+            for(Type caze : cases) {
+                Entry<Type,Type> caseIdentifier = new SimpleEntry<>(choiceType,caze);
+                HashSet<Type> caseChildren = new HashSet<>();
+                if(caze instanceof GeneratedTypeBuilder) {
+                    caze = ((GeneratedTypeBuilder) caze).toInstance();
+                }
+                collectAllContainerTypes((GeneratedType) caze, caseChildren);
+                for(Type caseChild : caseChildren) {
+                    childToCase.put(caseChild, caseIdentifier);
+                }
+            }
+        }
+        return ImmutableMap.copyOf(childToCase);
+
+    }
+
+    private static Type referencedType(final Type type) {
+        if(type instanceof ReferencedTypeImpl) {
+            return type;
+        }
+        return new ReferencedTypeImpl(type.getPackageName(), type.getName());
+    }
+
+    private static Set<Type> collectAllContainerTypes(final GeneratedType type, final Set<Type> collection) {
+        for (MethodSignature definition : type.getMethodDefinitions()) {
+            Type childType = definition.getReturnType();
+            if(childType instanceof ParameterizedType) {
+                childType = ((ParameterizedType) childType).getActualTypeArguments()[0];
+            }
+            if(childType instanceof GeneratedType || childType instanceof GeneratedTypeBuilder) {
+                collection.add(referencedType(childType));
+            }
+        }
+        for (Type parent : type.getImplements()) {
+            if (parent instanceof GeneratedType) {
+                collectAllContainerTypes((GeneratedType) parent, collection);
+            }
+        }
+        return collection;
+    }
+
+    private static final <T extends SchemaNode> T getOriginalSchema(final T choice) {
+        @SuppressWarnings("unchecked")
+        T original = (T) SchemaNodeUtils.getRootOriginalIfPossible(choice);
+        if(original != null) {
+            return original;
+        }
+        return choice;
+    }
+
+}
diff --git a/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/util/ClassLoaderUtils.java b/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/util/ClassLoaderUtils.java
deleted file mode 100644 (file)
index 01ed2f0..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * 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.sal.binding.generator.util;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.Callable;
-import java.util.concurrent.locks.Lock;
-
-import com.google.common.base.Joiner;
-import com.google.common.base.Optional;
-
-/**
- * @deprecated Use {@link org.opendaylight.yangtools.yang.binding.util.ClassLoaderUtils} instead.
- */
-@Deprecated
-public final class ClassLoaderUtils {
-
-    private ClassLoaderUtils() {
-        throw new UnsupportedOperationException("Utility class");
-    }
-
-    public static <V> V withClassLoader(final ClassLoader cls, final Callable<V> function) throws Exception {
-        checkNotNull(cls, "Classloader should not be null");
-        checkNotNull(function, "Function should not be null");
-
-        final ClassLoader oldCls = Thread.currentThread().getContextClassLoader();
-        try {
-            Thread.currentThread().setContextClassLoader(cls);
-            return function.call();
-        } finally {
-            Thread.currentThread().setContextClassLoader(oldCls);
-        }
-    }
-
-    public static <V> V withClassLoaderAndLock(final ClassLoader cls, final Lock lock, final Callable<V> function) throws Exception {
-        checkNotNull(lock, "Lock should not be null");
-
-        lock.lock();
-        try {
-            return withClassLoader(cls, function);
-        } finally {
-            lock.unlock();
-        }
-    }
-
-    /**
-     * @deprecated Use one of the other utility methods.
-     */
-    @Deprecated
-    public static <V> V withClassLoaderAndLock(final ClassLoader cls, final Optional<Lock> lock, final Callable<V> function) throws Exception {
-        if (lock.isPresent()) {
-            return withClassLoaderAndLock(cls, lock.get(), function);
-        } else {
-            return withClassLoader(cls, function);
-        }
-    }
-
-    public static Object construct(final Constructor<? extends Object> constructor, final List<Object> objects)
-            throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
-        Object[] initargs = objects.toArray(new Object[] {});
-        return constructor.newInstance(initargs);
-    }
-
-
-    public static Class<?> loadClass(final ClassLoader cls, final String name) throws ClassNotFoundException {
-        if ("byte[]".equals(name)) {
-            return byte[].class;
-        } else if("char[]".equals(name)) {
-            return char[].class;
-        }
-        try {
-            return cls.loadClass(name);
-        } catch (ClassNotFoundException e) {
-            String[] components = name.split("\\.");
-            String potentialOuter;
-            int length = components.length;
-            if (length > 2 && (potentialOuter = components[length - 2]) != null && Character.isUpperCase(potentialOuter.charAt(0))) {
-
-                    String outerName = Joiner.on(".").join(Arrays.asList(components).subList(0, length - 1));
-                    String innerName = outerName + "$" + components[length-1];
-                    return cls.loadClass(innerName);
-            } else {
-                throw e;
-            }
-        }
-    }
-
-    public static Class<?> loadClassWithTCCL(final String name) throws ClassNotFoundException {
-        return loadClass(Thread.currentThread().getContextClassLoader(), name);
-    }
-
-    public static Class<?> tryToLoadClassWithTCCL(final String fullyQualifiedName) {
-        try {
-            return loadClassWithTCCL(fullyQualifiedName);
-        } catch (ClassNotFoundException e) {
-            return null;
-        }
-    }
-}
index 1adbe05d3300cf587c4b8df4712f0f07747a840b..b80ada0f2f5a19c32519148541c6e83dd0e7c38e 100644 (file)
@@ -7,7 +7,9 @@
  */
 package org.opendaylight.yangtools.binding.generator.util.generated.type.builder;
 
-import java.util.ArrayList;
+import com.google.common.base.Preconditions;
+
+import java.util.Collections;
 import java.util.List;
 
 import org.opendaylight.yangtools.binding.generator.util.AbstractBaseType;
@@ -21,23 +23,22 @@ import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTO
 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTypeBuilder;
 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTypeBuilderBase;
 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.MethodSignatureBuilder;
-
-abstract class AbstractGeneratedTypeBuilder<T extends GeneratedTypeBuilderBase<T>> extends AbstractBaseType implements
-        GeneratedTypeBuilderBase<T> {
-
+import org.opendaylight.yangtools.util.LazyCollections;
+
+abstract class AbstractGeneratedTypeBuilder<T extends GeneratedTypeBuilderBase<T>> extends AbstractBaseType implements GeneratedTypeBuilderBase<T> {
+
+    private List<AnnotationTypeBuilder> annotationBuilders = Collections.emptyList();
+    private List<Type> implementsTypes = Collections.emptyList();
+    private List<EnumBuilder> enumDefinitions = Collections.emptyList();
+    private List<Constant> constants = Collections.emptyList();
+    private List<MethodSignatureBuilder> methodDefinitions = Collections.emptyList();
+    private final List<GeneratedTypeBuilder> enclosedTypes = Collections.emptyList();
+    private List<GeneratedTOBuilder> enclosedTransferObjects = Collections.emptyList();
+    private List<GeneratedPropertyBuilder> properties = Collections.emptyList();
     private String comment = "";
-
-    private final List<AnnotationTypeBuilder> annotationBuilders = new ArrayList<>();
-    private final List<Type> implementsTypes = new ArrayList<>();
-    private final List<EnumBuilder> enumDefinitions = new ArrayList<>();
-    private final List<Constant> constants = new ArrayList<>();
-    private final List<MethodSignatureBuilder> methodDefinitions = new ArrayList<>();
-    private final List<GeneratedTypeBuilder> enclosedTypes = new ArrayList<>();
-    private final List<GeneratedTOBuilder> enclosedTransferObjects = new ArrayList<>();
-    private final List<GeneratedPropertyBuilder> properties = new ArrayList<>();
     private boolean isAbstract;
 
-    public AbstractGeneratedTypeBuilder(final String packageName, final String name) {
+    protected AbstractGeneratedTypeBuilder(final String packageName, final String name) {
         super(packageName, name);
     }
 
@@ -49,10 +50,12 @@ abstract class AbstractGeneratedTypeBuilder<T extends GeneratedTypeBuilderBase<T
         return annotationBuilders;
     }
 
+    @Override
     public boolean isAbstract() {
         return isAbstract;
     }
 
+    @Override
     public List<Type> getImplementsTypes() {
         return implementsTypes;
     }
@@ -65,6 +68,7 @@ abstract class AbstractGeneratedTypeBuilder<T extends GeneratedTypeBuilderBase<T
         return constants;
     }
 
+    @Override
     public List<MethodSignatureBuilder> getMethodDefinitions() {
         return methodDefinitions;
     }
@@ -81,20 +85,17 @@ abstract class AbstractGeneratedTypeBuilder<T extends GeneratedTypeBuilderBase<T
 
     @Override
     public GeneratedTOBuilder addEnclosingTransferObject(final String name) {
-        if (name == null) {
-            throw new IllegalArgumentException("Name for Enclosing Generated Transfer Object cannot be null!");
-        }
+        Preconditions.checkArgument(name != null, "Name for Enclosing Generated Transfer Object cannot be null!");
         GeneratedTOBuilder builder = new GeneratedTOBuilderImpl(getFullyQualifiedName(), name);
-        enclosedTransferObjects.add(builder);
+
+        enclosedTransferObjects = LazyCollections.lazyAdd(enclosedTransferObjects, builder);
         return builder;
     }
 
     @Override
     public T addEnclosingTransferObject(final GeneratedTOBuilder genTOBuilder) {
-        if (genTOBuilder == null) {
-            throw new IllegalArgumentException("Parameter genTOBuilder cannot be null!");
-        }
-        enclosedTransferObjects.add(genTOBuilder);
+        Preconditions.checkArgument(genTOBuilder != null, "Parameter genTOBuilder cannot be null!");
+        enclosedTransferObjects = LazyCollections.lazyAdd(enclosedTransferObjects, genTOBuilder);
         return thisInstance();
     }
 
@@ -106,15 +107,11 @@ abstract class AbstractGeneratedTypeBuilder<T extends GeneratedTypeBuilderBase<T
 
     @Override
     public AnnotationTypeBuilder addAnnotation(final String packageName, final String name) {
-        if (packageName == null) {
-            throw new IllegalArgumentException("Package Name for Annotation Type cannot be null!");
-        }
-        if (name == null) {
-            throw new IllegalArgumentException("Name of Annotation Type cannot be null!");
-        }
+        Preconditions.checkArgument(packageName != null, "Package Name for Annotation Type cannot be null!");
+        Preconditions.checkArgument(name != null, "Name of Annotation Type cannot be null!");
 
         final AnnotationTypeBuilder builder = new AnnotationTypeBuilderImpl(packageName, name);
-        annotationBuilders.add(builder);
+        annotationBuilders = LazyCollections.lazyAdd(annotationBuilders, builder);
         return builder;
     }
 
@@ -126,54 +123,42 @@ abstract class AbstractGeneratedTypeBuilder<T extends GeneratedTypeBuilderBase<T
 
     @Override
     public T addImplementsType(final Type genType) {
-        if (genType == null) {
-            throw new IllegalArgumentException("Type cannot be null");
-        }
-        implementsTypes.add(genType);
+        Preconditions.checkArgument(genType != null, "Type cannot be null");
+        implementsTypes = LazyCollections.lazyAdd(implementsTypes, genType);
         return thisInstance();
     }
 
     @Override
     public Constant addConstant(final Type type, final String name, final Object value) {
-        if (type == null) {
-            throw new IllegalArgumentException("Returning Type for Constant cannot be null!");
-        }
-        if (name == null) {
-            throw new IllegalArgumentException("Name of constant cannot be null!");
-        }
+        Preconditions.checkArgument(type != null, "Returning Type for Constant cannot be null!");
+        Preconditions.checkArgument(name != null, "Name of constant cannot be null!");
 
         final Constant constant = new ConstantImpl(this, type, name, value);
-        constants.add(constant);
+        constants = LazyCollections.lazyAdd(constants, constant);
         return constant;
     }
 
     @Override
     public EnumBuilder addEnumeration(final String name) {
-        if (name == null) {
-            throw new IllegalArgumentException("Name of enumeration cannot be null!");
-        }
+        Preconditions.checkArgument(name != null, "Name of enumeration cannot be null!");
         final EnumBuilder builder = new EnumerationBuilderImpl(getFullyQualifiedName(), name);
-        enumDefinitions.add(builder);
+        enumDefinitions = LazyCollections.lazyAdd(enumDefinitions, builder);
         return builder;
     }
 
     @Override
     public MethodSignatureBuilder addMethod(final String name) {
-        if (name == null) {
-            throw new IllegalArgumentException("Name of method cannot be null!");
-        }
+        Preconditions.checkArgument(name != null, "Name of method cannot be null!");
         final MethodSignatureBuilder builder = new MethodSignatureBuilderImpl(name);
         builder.setAccessModifier(AccessModifier.PUBLIC);
         builder.setAbstract(true);
-        methodDefinitions.add(builder);
+        methodDefinitions = LazyCollections.lazyAdd(methodDefinitions, builder);
         return builder;
     }
 
     @Override
     public boolean containsMethod(final String name) {
-        if (name == null) {
-            throw new IllegalArgumentException("Parameter name can't be null");
-        }
+        Preconditions.checkArgument(name != null, "Parameter name can't be null");
         for (MethodSignatureBuilder methodDefinition : methodDefinitions) {
             if (name.equals(methodDefinition.getName())) {
                 return true;
@@ -186,15 +171,13 @@ abstract class AbstractGeneratedTypeBuilder<T extends GeneratedTypeBuilderBase<T
     public GeneratedPropertyBuilder addProperty(final String name) {
         final GeneratedPropertyBuilder builder = new GeneratedPropertyBuilderImpl(name);
         builder.setAccessModifier(AccessModifier.PUBLIC);
-        properties.add(builder);
+        properties = LazyCollections.lazyAdd(properties, builder);
         return builder;
     }
 
     @Override
     public boolean containsProperty(final String name) {
-        if (name == null) {
-            throw new IllegalArgumentException("Parameter name can't be null");
-        }
+        Preconditions.checkArgument(name != null, "Parameter name can't be null");
         for (GeneratedPropertyBuilder property : properties) {
             if (name.equals(property.getName())) {
                 return true;
@@ -245,6 +228,7 @@ abstract class AbstractGeneratedTypeBuilder<T extends GeneratedTypeBuilderBase<T
         return null;
     }
 
+    @Override
     public List<GeneratedPropertyBuilder> getProperties() {
         return properties;
     }
index cfadce1a5e6f36938bbaacc8f06ea5af7759c0de..18798faf0683b600c76b47ffa683ac8b4d597f7f 100644 (file)
@@ -7,8 +7,6 @@
  */
 package org.opendaylight.yangtools.binding.generator.util.generated.type.builder;
 
-
-import java.util.Collections;
 import java.util.List;
 
 import org.opendaylight.yangtools.sal.binding.model.api.AccessModifier;
@@ -27,13 +25,13 @@ abstract class AbstractTypeMember implements TypeMember {
     private final boolean isStatic;
     private final AccessModifier accessModifier;
 
-    public AbstractTypeMember(final Type definingType, final String name,  final List<AnnotationType> annotations,
-                              final String comment, final AccessModifier accessModifier, final Type returnType,
-                              final boolean isFinal, final boolean isStatic) {
+    protected AbstractTypeMember(final Type definingType, final String name,  final List<AnnotationType> annotations,
+            final String comment, final AccessModifier accessModifier, final Type returnType,
+            final boolean isFinal, final boolean isStatic) {
         super();
         this.definingType = definingType;
         this.name = name;
-        this.annotations = annotations.isEmpty() ? Collections.<AnnotationType>emptyList() : Collections.unmodifiableList(annotations);
+        this.annotations = annotations;
         this.comment = comment;
         this.accessModifier = accessModifier;
         this.returnType = returnType;
index 7b809ebb0182d7feea4aaddd74786ed36c3a970a..cb84ac2ea8d2a5a50c80665f7dd0a3899fc3a9d3 100644 (file)
@@ -7,7 +7,11 @@
  */
 package org.opendaylight.yangtools.binding.generator.util.generated.type.builder;
 
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 import org.opendaylight.yangtools.sal.binding.model.api.AccessModifier;
@@ -15,11 +19,12 @@ import org.opendaylight.yangtools.sal.binding.model.api.AnnotationType;
 import org.opendaylight.yangtools.sal.binding.model.api.Type;
 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.AnnotationTypeBuilder;
 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.TypeMemberBuilder;
+import org.opendaylight.yangtools.util.LazyCollections;
 
 abstract class AbstractTypeMemberBuilder<T extends TypeMemberBuilder<T>> implements TypeMemberBuilder<T> {
     private final String name;
     private Type returnType;
-    private final List<AnnotationTypeBuilder> annotationBuilders;
+    private List<AnnotationTypeBuilder> annotationBuilders = Collections.emptyList();
     private String comment = "";
     private boolean isFinal;
     private boolean isStatic;
@@ -27,20 +32,15 @@ abstract class AbstractTypeMemberBuilder<T extends TypeMemberBuilder<T>> impleme
 
     public AbstractTypeMemberBuilder(final String name) {
         this.name = name;
-        this.annotationBuilders = new ArrayList<>();
     }
 
     @Override
-    public AnnotationTypeBuilder addAnnotation(String packageName, String name) {
-        if (packageName == null) {
-            throw new IllegalArgumentException("Annotation Type cannot have package name null!");
-        }
-        if (name == null) {
-            throw new IllegalArgumentException("Annotation Type cannot have name as null!");
-        }
-        final AnnotationTypeBuilder builder = new AnnotationTypeBuilderImpl(
-                    packageName, name);
-        annotationBuilders.add(builder);
+    public AnnotationTypeBuilder addAnnotation(final String packageName, final String name) {
+        Preconditions.checkArgument(packageName != null, "Annotation Type cannot have package name null!");
+        Preconditions.checkArgument(name != null, "Annotation Type cannot have name as null!");
+
+        final AnnotationTypeBuilder builder = new AnnotationTypeBuilderImpl(packageName, name);
+        annotationBuilders = LazyCollections.lazyAdd(annotationBuilders, builder);
         return builder;
     }
 
@@ -48,7 +48,7 @@ abstract class AbstractTypeMemberBuilder<T extends TypeMemberBuilder<T>> impleme
         return returnType;
     }
 
-    protected List<AnnotationTypeBuilder> getAnnotationBuilders() {
+    protected Iterable<AnnotationTypeBuilder> getAnnotationBuilders() {
         return annotationBuilders;
     }
 
@@ -77,25 +77,21 @@ abstract class AbstractTypeMemberBuilder<T extends TypeMemberBuilder<T>> impleme
     protected abstract T thisInstance();
 
     @Override
-    public T setReturnType(Type returnType) {
-        if (returnType == null) {
-            throw new IllegalArgumentException("Return Type of member cannot be null!");
-        }
+    public T setReturnType(final Type returnType) {
+        Preconditions.checkArgument(returnType != null, "Return Type of member cannot be null!");
         this.returnType = returnType;
         return thisInstance();
     }
 
     @Override
-    public T setAccessModifier(AccessModifier modifier) {
-        if (modifier == null) {
-            throw new IllegalArgumentException("Access Modifier for member type cannot be null!");
-        }
+    public T setAccessModifier(final AccessModifier modifier) {
+        Preconditions.checkArgument(modifier != null, "Access Modifier for member type cannot be null!");
         this.accessModifier = modifier;
         return thisInstance();
     }
 
     @Override
-    public T setComment(String comment) {
+    public T setComment(final String comment) {
         if (comment == null) {
             this.comment = "";
         }
@@ -104,13 +100,13 @@ abstract class AbstractTypeMemberBuilder<T extends TypeMemberBuilder<T>> impleme
     }
 
     @Override
-    public T setFinal(boolean isFinal) {
+    public T setFinal(final boolean isFinal) {
         this.isFinal = isFinal;
         return thisInstance();
     }
 
     @Override
-    public T setStatic(boolean isStatic) {
+    public T setStatic(final boolean isStatic) {
         this.isStatic = isStatic;
         return thisInstance();
     }
@@ -122,7 +118,8 @@ abstract class AbstractTypeMemberBuilder<T extends TypeMemberBuilder<T>> impleme
                 annotations.add(annotBuilder.toInstance());
             }
         }
-        return annotations;
+
+        return ImmutableList.copyOf(annotations);
     }
 
     @Override
@@ -136,7 +133,7 @@ abstract class AbstractTypeMemberBuilder<T extends TypeMemberBuilder<T>> impleme
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(final Object obj) {
         if (this == obj) {
             return true;
         }
index 0c617152c64f0414811fe962797fcab3741d6f29..df546ed30e73c3076f44ac7bebe936ed1bf400b0 100644 (file)
@@ -7,6 +7,8 @@
  */
 package org.opendaylight.yangtools.binding.generator.util.generated.type.builder;
 
+import com.google.common.collect.ImmutableList;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -14,45 +16,56 @@ import java.util.List;
 import org.opendaylight.yangtools.binding.generator.util.AbstractBaseType;
 import org.opendaylight.yangtools.sal.binding.model.api.AnnotationType;
 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.AnnotationTypeBuilder;
+import org.opendaylight.yangtools.util.LazyCollections;
 
 final class AnnotationTypeBuilderImpl extends AbstractBaseType implements AnnotationTypeBuilder {
-    
+
     private final String packageName;
     private final String name;
-    private final List<AnnotationTypeBuilder> annotationBuilders;
-    private final List<AnnotationType.Parameter> parameters;
-    
+    private List<AnnotationTypeBuilder> annotationBuilders = Collections.emptyList();
+    private List<AnnotationType.Parameter> parameters = Collections.emptyList();
+
     public AnnotationTypeBuilderImpl(final String packageName, final String name) {
         super(packageName, name);
         this.packageName = packageName;
         this.name = name;
-        annotationBuilders = new ArrayList<>();
-        parameters = new ArrayList<>();
     }
 
     @Override
     public AnnotationTypeBuilder addAnnotation(final String packageName, final String name) {
         if (packageName != null && name != null) {
             final AnnotationTypeBuilder builder = new AnnotationTypeBuilderImpl(packageName, name);
-            if (annotationBuilders.add(builder)) {
+            if (!annotationBuilders.contains(builder)) {
+                annotationBuilders = LazyCollections.lazyAdd(annotationBuilders, builder);
                 return builder;
             }
         }
         return null;
     }
 
+    private boolean addParameter(final ParameterImpl param) {
+        if (!parameters.contains(param)) {
+            parameters = LazyCollections.lazyAdd(parameters, param);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
     @Override
-    public boolean addParameter(String paramName, String value) {
-        if ((paramName != null) && (value != null)) {
-            return parameters.add(new ParameterImpl(paramName, value));
+    public boolean addParameter(final String paramName, final String value) {
+        if (paramName != null && value != null) {
+            final ParameterImpl param = new ParameterImpl(paramName, value);
+            return addParameter(param);
         }
         return false;
     }
 
     @Override
-    public boolean addParameters(String paramName, List<String> values) {
-        if ((paramName != null) && (values != null)) {
-            return parameters.add(new ParameterImpl(paramName, values));
+    public boolean addParameters(final String paramName, final List<String> values) {
+        if (paramName != null && values != null) {
+            final ParameterImpl param = new ParameterImpl(paramName, values);
+            return addParameter(param);
         }
         return false;
     }
@@ -73,7 +86,7 @@ final class AnnotationTypeBuilderImpl extends AbstractBaseType implements Annota
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(final Object obj) {
         if (this == obj) {
             return true;
         }
@@ -115,37 +128,38 @@ final class AnnotationTypeBuilderImpl extends AbstractBaseType implements Annota
         builder.append("]");
         return builder.toString();
     }
-    
+
     private static final class AnnotationTypeImpl implements AnnotationType {
-        
+
         private final String packageName;
         private final String name;
-        private List<AnnotationType> annotations;
+        private final List<AnnotationType> annotations;
         private final List<AnnotationType.Parameter> parameters;
-        private List<String> paramNames;
-        
-        public AnnotationTypeImpl(String packageName, String name,
-                List<AnnotationTypeBuilder> annotationBuilders,
-                List<AnnotationType.Parameter> parameters) {
+        private final List<String> paramNames;
+
+        public AnnotationTypeImpl(final String packageName, final String name,
+                final List<AnnotationTypeBuilder> annotationBuilders,
+                final List<AnnotationType.Parameter> parameters) {
             super();
             this.packageName = packageName;
             this.name = name;
-            
-            this.annotations = new ArrayList<>();
+
+            final List<AnnotationType> a = new ArrayList<>();
             for (final AnnotationTypeBuilder builder : annotationBuilders) {
-                annotations.add(builder.toInstance());
+                a.add(builder.toInstance());
             }
-            
-            this.annotations = Collections.unmodifiableList(annotations); 
-            this.parameters = Collections.unmodifiableList(parameters);
-            
-            paramNames = new ArrayList<>();
+            this.annotations = ImmutableList.copyOf(a);
+
+            final List<String> p = new ArrayList<>();
             for (final AnnotationType.Parameter parameter : parameters) {
-                paramNames.add(parameter.getName());
+                p.add(parameter.getName());
             }
-            this.paramNames = Collections.unmodifiableList(paramNames);
+            this.paramNames = ImmutableList.copyOf(p);
+
+            this.parameters = parameters.isEmpty() ? Collections.<AnnotationType.Parameter>emptyList()
+                    : Collections.unmodifiableList(parameters);
         }
-        
+
         @Override
         public String getPackageName() {
             return packageName;
@@ -187,12 +201,12 @@ final class AnnotationTypeBuilderImpl extends AbstractBaseType implements Annota
         public List<String> getParameterNames() {
             return paramNames;
         }
-        
+
         @Override
         public boolean containsParameters() {
             return !parameters.isEmpty();
         }
-        
+
         @Override
         public int hashCode() {
             final int prime = 31;
@@ -204,7 +218,7 @@ final class AnnotationTypeBuilderImpl extends AbstractBaseType implements Annota
         }
 
         @Override
-        public boolean equals(Object obj) {
+        public boolean equals(final Object obj) {
             if (this == obj) {
                 return true;
             }
@@ -247,21 +261,21 @@ final class AnnotationTypeBuilderImpl extends AbstractBaseType implements Annota
             return builder.toString();
         }
     }
-    
+
     private static final class ParameterImpl implements AnnotationType.Parameter {
-        
+
         private final String name;
         private final String value;
         private final List<String> values;
-        
-        public ParameterImpl(String name, String value) {
+
+        public ParameterImpl(final String name, final String value) {
             super();
             this.name = name;
             this.value = value;
             this.values = Collections.emptyList();
         }
-        
-        public ParameterImpl(String name, List<String> values) {
+
+        public ParameterImpl(final String name, final List<String> values) {
             super();
             this.name = name;
             this.values = values;
@@ -292,7 +306,7 @@ final class AnnotationTypeBuilderImpl extends AbstractBaseType implements Annota
         }
 
         @Override
-        public boolean equals(Object obj) {
+        public boolean equals(final Object obj) {
             if (this == obj) {
                 return true;
             }
index be8d905e6baafa1a4690bd6d40d3aa6246cce785..cb7f50549e537e6a24f11e5407f35082b8f970ae 100644 (file)
@@ -7,20 +7,22 @@
  */
 package org.opendaylight.yangtools.binding.generator.util.generated.type.builder;
 
+import com.google.common.collect.ImmutableList;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
-
 import org.opendaylight.yangtools.binding.generator.util.AbstractBaseType;
 import org.opendaylight.yangtools.sal.binding.model.api.AnnotationType;
 import org.opendaylight.yangtools.sal.binding.model.api.Constant;
 import org.opendaylight.yangtools.sal.binding.model.api.Enumeration;
+import org.opendaylight.yangtools.sal.binding.model.api.Enumeration.Pair;
 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty;
 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType;
 import org.opendaylight.yangtools.sal.binding.model.api.MethodSignature;
 import org.opendaylight.yangtools.sal.binding.model.api.Type;
 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.AnnotationTypeBuilder;
 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.EnumBuilder;
+import org.opendaylight.yangtools.util.LazyCollections;
 import org.opendaylight.yangtools.yang.binding.BindingMapping;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.model.api.Status;
@@ -30,8 +32,9 @@ import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPai
 public final class EnumerationBuilderImpl extends AbstractBaseType implements EnumBuilder {
     private final String packageName;
     private final String name;
-    private final List<Enumeration.Pair> values;
-    private final List<AnnotationTypeBuilder> annotationBuilders = new ArrayList<>();
+    private List<Enumeration.Pair> values = Collections.emptyList();
+    private List<AnnotationTypeBuilder> annotationBuilders = Collections.emptyList();
+    private List<Pair> unmodifiableValues  = Collections.emptyList();
     private String description;
     private String reference;
     private String moduleName;
@@ -41,7 +44,6 @@ public final class EnumerationBuilderImpl extends AbstractBaseType implements En
         super(packageName, name);
         this.packageName = packageName;
         this.name = name;
-        values = new ArrayList<>();
     }
 
     public void setReference(final String reference) {
@@ -66,7 +68,8 @@ public final class EnumerationBuilderImpl extends AbstractBaseType implements En
     public AnnotationTypeBuilder addAnnotation(final String packageName, final String name) {
         if (packageName != null && name != null) {
             final AnnotationTypeBuilder builder = new AnnotationTypeBuilderImpl(packageName, name);
-            if (annotationBuilders.add(builder)) {
+            if (!annotationBuilders.contains(builder)) {
+                annotationBuilders = LazyCollections.lazyAdd(annotationBuilders, builder);
                 return builder;
             }
         }
@@ -75,39 +78,17 @@ public final class EnumerationBuilderImpl extends AbstractBaseType implements En
 
     @Override
     public void addValue(final String name, final Integer value, final String description) {
-        values.add(new EnumPairImpl(name, value, description));
+        final EnumPairImpl p = new EnumPairImpl(name, value, description);
+        values = LazyCollections.lazyAdd(values, p);
+        unmodifiableValues = Collections.unmodifiableList(values);
     }
 
     @Override
     public Enumeration toInstance(final Type definingType) {
-        return new EnumerationImpl(definingType, annotationBuilders, packageName, name, values,
+        return new EnumerationImpl(definingType, annotationBuilders, packageName, name, unmodifiableValues,
                 description, reference, moduleName, schemaPath);
     }
 
-    @Override
-    public void updateEnumPairsFromEnumTypeDef(final EnumTypeDefinition enumTypeDef) {
-        final List<EnumPair> enums = enumTypeDef.getValues();
-        if (enums != null) {
-            int listIndex = 0;
-            for (final EnumPair enumPair : enums) {
-                if (enumPair != null) {
-                    final String enumPairName = BindingMapping.getClassName(enumPair.getName());
-                    Integer enumPairValue = enumPair.getValue();
-
-                    if (enumPairValue == null) {
-                        enumPairValue = listIndex;
-                    }
-                    else {
-                        listIndex = enumPairValue;
-                    }
-
-                    this.addValue(enumPairName, enumPairValue, enumPair.getDescription());
-                    listIndex++;
-                }
-            }
-        }
-    }
-
     /*
      * (non-Javadoc)
      *
@@ -128,7 +109,7 @@ public final class EnumerationBuilderImpl extends AbstractBaseType implements En
      * @see java.lang.Object#equals(java.lang.Object)
      */
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(final Object obj) {
         if (this == obj) {
             return true;
         }
@@ -174,13 +155,38 @@ public final class EnumerationBuilderImpl extends AbstractBaseType implements En
         return builder.toString();
     }
 
+    @Override
+    public void updateEnumPairsFromEnumTypeDef(final EnumTypeDefinition enumTypeDef) {
+        final List<EnumPair> enums = enumTypeDef.getValues();
+        if (enums != null) {
+            int listIndex = 0;
+            for (final EnumPair enumPair : enums) {
+                if (enumPair != null) {
+                    final String enumPairName = BindingMapping.getClassName(enumPair.getName());
+                    Integer enumPairValue = enumPair.getValue();
+
+                    if (enumPairValue == null) {
+                        enumPairValue = listIndex;
+                    }
+                    else {
+                        listIndex = enumPairValue;
+                    }
+
+                    this.addValue(enumPairName, enumPairValue, enumPair.getDescription());
+                    listIndex++;
+                }
+            }
+        }
+
+    }
+
     private static final class EnumPairImpl implements Enumeration.Pair {
 
         private final String name;
         private final Integer value;
         private final String description;
 
-        public EnumPairImpl(String name, Integer value, String description) {
+        public EnumPairImpl(final String name, final Integer value, final String description) {
             super();
             this.name = name;
             this.value = value;
@@ -217,7 +223,7 @@ public final class EnumerationBuilderImpl extends AbstractBaseType implements En
          * @see java.lang.Object#equals(java.lang.Object)
          */
         @Override
-        public boolean equals(Object obj) {
+        public boolean equals(final Object obj) {
             if (this == obj) {
                 return true;
             }
@@ -289,24 +295,26 @@ public final class EnumerationBuilderImpl extends AbstractBaseType implements En
         private final String moduleName;
         private final Iterable<QName> schemaPath;
         private final List<Pair> values;
-        private List<AnnotationType> annotations = new ArrayList<>();
+        private final List<AnnotationType> annotations;
 
         public EnumerationImpl(final Type definingType, final List<AnnotationTypeBuilder> annotationBuilders,
                 final String packageName, final String name, final List<Pair> values, final String description,
                 final String reference, final String moduleName, final Iterable<QName> schemaPath) {
             super();
             this.definingType = definingType;
-            for (final AnnotationTypeBuilder builder : annotationBuilders) {
-                annotations.add(builder.toInstance());
-            }
-            this.annotations = Collections.unmodifiableList(annotations);
             this.packageName = packageName;
+            this.values = values;
             this.name = name;
-            this.values = Collections.unmodifiableList(values);
             this.description = description;
-            this.reference = reference;
             this.moduleName = moduleName;
             this.schemaPath = schemaPath;
+            this.reference = reference;
+
+            final ArrayList<AnnotationType> a = new ArrayList<>();
+            for (final AnnotationTypeBuilder builder : annotationBuilders) {
+                a.add(builder.toInstance());
+            }
+            this.annotations = ImmutableList.copyOf(a);
         }
 
         @Override
@@ -389,7 +397,7 @@ public final class EnumerationBuilderImpl extends AbstractBaseType implements En
          * @see java.lang.Object#equals(java.lang.Object)
          */
         @Override
-        public boolean equals(Object obj) {
+        public boolean equals(final Object obj) {
             if (this == obj) {
                 return true;
             }
index 18efb661d6b980417f5abc2ac898e8c856895332..270b04ac81161374d337fe03f012d96691939920 100644 (file)
@@ -7,9 +7,9 @@
  */
 package org.opendaylight.yangtools.binding.generator.util.generated.type.builder;
 
-import java.util.ArrayList;
+import com.google.common.base.Preconditions;
+import java.util.Collections;
 import java.util.List;
-
 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.ParameterizedType;
@@ -18,15 +18,15 @@ import org.opendaylight.yangtools.sal.binding.model.api.Type;
 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedPropertyBuilder;
 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTOBuilder;
 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.MethodSignatureBuilder;
+import org.opendaylight.yangtools.util.LazyCollections;
 import org.opendaylight.yangtools.yang.common.QName;
 
-public final class GeneratedTOBuilderImpl extends AbstractGeneratedTypeBuilder<GeneratedTOBuilder> implements
-        GeneratedTOBuilder {
+public final class GeneratedTOBuilderImpl extends AbstractGeneratedTypeBuilder<GeneratedTOBuilder> implements GeneratedTOBuilder {
 
     private GeneratedTransferObject extendsType;
-    private final ArrayList<GeneratedPropertyBuilder> equalsProperties = new ArrayList<>();
-    private final ArrayList<GeneratedPropertyBuilder> hashProperties = new ArrayList<>();
-    private final ArrayList<GeneratedPropertyBuilder> toStringProperties = new ArrayList<>();
+    private List<GeneratedPropertyBuilder> equalsProperties = Collections.emptyList();
+    private List<GeneratedPropertyBuilder> hashProperties = Collections.emptyList();
+    private List<GeneratedPropertyBuilder> toStringProperties = Collections.emptyList();
     private boolean isTypedef = false;
     private boolean isUnionType = false;
     private boolean isUnionTypeBuilder = false;
@@ -44,9 +44,7 @@ public final class GeneratedTOBuilderImpl extends AbstractGeneratedTypeBuilder<G
 
     @Override
     public GeneratedTOBuilder setExtendsType(final GeneratedTransferObject genTransObj) {
-        if (genTransObj == null) {
-            throw new IllegalArgumentException("Generated Transfer Object cannot be null!");
-        }
+        Preconditions.checkArgument(genTransObj != null, "Generated Transfer Object cannot be null!");
         extendsType = genTransObj;
         return this;
     }
@@ -74,19 +72,19 @@ public final class GeneratedTOBuilderImpl extends AbstractGeneratedTypeBuilder<G
 
     @Override
     public GeneratedTOBuilder addEqualsIdentity(final GeneratedPropertyBuilder property) {
-        equalsProperties.add(property);
+        equalsProperties = LazyCollections.lazyAdd(equalsProperties, property);
         return this;
     }
 
     @Override
     public GeneratedTOBuilder addHashIdentity(final GeneratedPropertyBuilder property) {
-        hashProperties.add(property);
+        hashProperties = LazyCollections.lazyAdd(hashProperties, property);
         return this;
     }
 
     @Override
     public GeneratedTOBuilder addToStringProperty(final GeneratedPropertyBuilder property) {
-        toStringProperties.add(property);
+        toStringProperties = LazyCollections.lazyAdd(toStringProperties, property);
         return this;
     }
 
@@ -107,8 +105,6 @@ public final class GeneratedTOBuilderImpl extends AbstractGeneratedTypeBuilder<G
 
     @Override
     public GeneratedTransferObject toInstance() {
-        // FIXME: can we compact the arrays now? It needs to be thread-safe,
-        // though
         return new GeneratedTransferObjectImpl(this);
     }
 
@@ -175,7 +171,7 @@ public final class GeneratedTOBuilderImpl extends AbstractGeneratedTypeBuilder<G
     }
 
     private static final class GeneratedTransferObjectImpl extends AbstractGeneratedType implements
-            GeneratedTransferObject {
+    GeneratedTransferObject {
 
         private final List<GeneratedProperty> equalsProperties;
         private final List<GeneratedProperty> hashCodeProperties;
@@ -194,9 +190,13 @@ public final class GeneratedTOBuilderImpl extends AbstractGeneratedTypeBuilder<G
         public GeneratedTransferObjectImpl(final GeneratedTOBuilderImpl builder) {
             super(builder);
             this.extendsType = builder.extendsType;
+
+            // FIXME: if these fields were guaranteed to be constant, we could perhaps
+            //        cache and reuse them between instances...
             this.equalsProperties = toUnmodifiableProperties(builder.equalsProperties);
             this.hashCodeProperties = toUnmodifiableProperties(builder.hashProperties);
             this.stringProperties = toUnmodifiableProperties(builder.toStringProperties);
+
             this.isTypedef = builder.isTypedef;
             this.isUnionType = builder.isUnionType;
             this.isUnionTypeBuilder = builder.isUnionTypeBuilder;
index a5acd30407bdcfba5dd198aa106b350ecc01b090..ea0ff07d2ace4e7c2cd9efb94320f552726de8e6 100644 (file)
@@ -7,46 +7,48 @@
  */
 package org.opendaylight.yangtools.binding.generator.util.generated.type.builder;
 
-import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 import org.opendaylight.yangtools.sal.binding.model.api.AnnotationType;
 import org.opendaylight.yangtools.sal.binding.model.api.MethodSignature;
 import org.opendaylight.yangtools.sal.binding.model.api.Type;
 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.MethodSignatureBuilder;
+import org.opendaylight.yangtools.util.LazyCollections;
 
 final class MethodSignatureBuilderImpl extends AbstractTypeMemberBuilder<MethodSignatureBuilder> implements MethodSignatureBuilder {
 
-    private final List<MethodSignature.Parameter> parameters;
+    private List<MethodSignature.Parameter> parameters = Collections.emptyList();
+    private List<MethodSignature.Parameter> unmodifiableParams  = Collections.emptyList();
     private boolean isAbstract;
 
     public MethodSignatureBuilderImpl(final String name) {
         super(name);
-        this.parameters = new ArrayList<>();
     }
 
     @Override
-    public MethodSignatureBuilder setAbstract(boolean isAbstract) {
+    public MethodSignatureBuilder setAbstract(final boolean isAbstract) {
         this.isAbstract = isAbstract;
         return this;
     }
 
     @Override
-    public MethodSignatureBuilder addParameter(Type type, String name) {
-        parameters.add(new MethodParameterImpl(name, type));
+    public MethodSignatureBuilder addParameter(final Type type, final String name) {
+        parameters = LazyCollections.lazyAdd(parameters, new MethodParameterImpl(name, type));
+        unmodifiableParams = Collections.unmodifiableList(parameters);
         return this;
     }
 
-@Override
+    @Override
     protected MethodSignatureBuilder thisInstance() {
         return this;
     }
 
     @Override
-    public MethodSignature toInstance(Type definingType) {
+    public MethodSignature toInstance(final Type definingType) {
         final List<AnnotationType> annotations = toAnnotationTypes();
         return new MethodSignatureImpl(definingType, getName(), annotations, getComment(), getAccessModifier(),
-                getReturnType(), parameters, isFinal(), isAbstract, isStatic());
+                getReturnType(), unmodifiableParams, isFinal(), isAbstract, isStatic());
     }
 
     @Override
@@ -60,7 +62,7 @@ final class MethodSignatureBuilderImpl extends AbstractTypeMemberBuilder<MethodS
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(final Object obj) {
         if (this == obj) {
             return true;
         }
index 9672b4d2219bb86f1abf22ca76aad4c187394e99..cf0f7ea1d807ba2d89bb00be2c246361b603b186 100644 (file)
@@ -7,7 +7,6 @@
  */
 package org.opendaylight.yangtools.binding.generator.util.generated.type.builder;
 
-import java.util.Collections;
 import java.util.List;
 
 import org.opendaylight.yangtools.sal.binding.model.api.AccessModifier;
@@ -21,12 +20,12 @@ class MethodSignatureImpl extends AbstractTypeMember implements MethodSignature
     private final boolean isAbstract;
 
     public MethodSignatureImpl(final Type definingType, final String name,
-                               final List<AnnotationType> annotations,
-                               final String comment, final AccessModifier accessModifier,
-                               final Type returnType, final List<Parameter> params, boolean isFinal,
-                               boolean isAbstract, boolean isStatic) {
+            final List<AnnotationType> annotations,
+            final String comment, final AccessModifier accessModifier,
+            final Type returnType, final List<Parameter> params, final boolean isFinal,
+            final boolean isAbstract, final boolean isStatic) {
         super(definingType, name, annotations, comment, accessModifier, returnType, isFinal, isStatic);
-        this.params = Collections.unmodifiableList(params);
+        this.params = params;
         this.isAbstract = isAbstract;
     }
 
@@ -53,7 +52,7 @@ class MethodSignatureImpl extends AbstractTypeMember implements MethodSignature
     }
 
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(final Object obj) {
         if (this == obj) {
             return true;
         }
index 24d0a7fd6031f30b2410033337f7f6bef89f59f4..eb7b948c545283c6e7e3d162a202574365eb0723 100644 (file)
@@ -498,15 +498,15 @@ abstract class BaseTemplate {
             Â«val numberClass = restrictions.lengthConstraints.iterator.next.min.class»
             public static Â«List.importedName»<«Range.importedName»<«numberClass.importedNumber»>> Â«methodName»() {
                 Â«IF numberClass.equals(typeof(BigDecimal))»
-                    Â«lengthMethodBody(restrictions, numberClass, className, varName)»
+                    Â«lengthBody(restrictions, numberClass, className, varName)»
                 Â«ELSE»
-                    Â«lengthMethodBody(restrictions, typeof(BigInteger), className, varName)»
+                    Â«lengthBody(restrictions, typeof(BigInteger), className, varName)»
                 Â«ENDIF»
             }
         Â«ENDIF»
     '''
 
-    def private lengthMethodBody(Restrictions restrictions, Class<? extends Number> numberClass, String className, String varName) '''
+    def private lengthBody(Restrictions restrictions, Class<? extends Number> numberClass, String className, String varName) '''
         if («varName» == null) {
             synchronized («className».class) {
                 if («varName» == null) {
@@ -526,9 +526,9 @@ abstract class BaseTemplate {
             Â«val number = returnType.importedNumber»
             public static Â«List.importedName»<«Range.importedName»<«number»>> Â«methodName»() {
                 Â«IF returnType.fullyQualifiedName.equals(BigDecimal.canonicalName)»
-                    Â«rangeMethodBody(restrictions, BigDecimal, className, varName)»
+                    Â«rangeBody(restrictions, BigDecimal, className, varName)»
                 Â«ELSE»
-                    Â«rangeMethodBody(restrictions, BigInteger, className, varName)»
+                    Â«rangeBody(restrictions, BigInteger, className, varName)»
                 Â«ENDIF»
             }
         Â«ENDIF»
@@ -539,15 +539,15 @@ abstract class BaseTemplate {
             Â«val returnType = properties.iterator.next.returnType»
             public static Â«List.importedName»<«Range.importedName»<«returnType.importedNumber»>> Â«methodName»() {
                 Â«IF returnType.fullyQualifiedName.equals(BigDecimal.canonicalName)»
-                    Â«rangeMethodBody(restrictions, BigDecimal, className, varName)»
+                    Â«rangeBody(restrictions, BigDecimal, className, varName)»
                 Â«ELSE»
-                    Â«rangeMethodBody(restrictions, BigInteger, className, varName)»
+                    Â«rangeBody(restrictions, BigInteger, className, varName)»
                 Â«ENDIF»
             }
         Â«ENDIF»
     '''
 
-    def private rangeMethodBody(Restrictions restrictions, Class<? extends Number> numberClass, String className, String varName) '''
+    def private rangeBody(Restrictions restrictions, Class<? extends Number> numberClass, String className, String varName) '''
         if («varName» == null) {
             synchronized («className».class) {
                 if («varName» == null) {
@@ -576,7 +576,7 @@ abstract class BaseTemplate {
         return BigInteger.importedName
     }
 
-    def private String numericValue(Class<? extends Number> clazz, Object numberValue) {
+    def protected String numericValue(Class<? extends Number> clazz, Object numberValue) {
         val number = clazz.importedName;
         val value = numberValue.toString
         if (clazz.equals(typeof(BigInteger)) || clazz.equals(typeof(BigDecimal))) {
index e92f84e489ae1e3db0bea4faf4569b3ae2e8f304..afebd5846b156465d9ec7de1049124190696a7a1 100644 (file)
@@ -7,24 +7,28 @@
  */
 package org.opendaylight.yangtools.sal.java.api.generator
 
+import com.google.common.collect.ImmutableList
+import com.google.common.collect.Lists
+import com.google.common.collect.Range
+import com.google.common.io.BaseEncoding
+import java.beans.ConstructorProperties
+import java.math.BigDecimal
+import java.math.BigInteger
+import java.util.ArrayList
+import java.util.Arrays
+import java.util.Collections
 import java.util.List
+import java.util.regex.Pattern
 import org.opendaylight.yangtools.binding.generator.util.TypeConstants
 import org.opendaylight.yangtools.sal.binding.model.api.Constant
 import org.opendaylight.yangtools.sal.binding.model.api.Enumeration
 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
-import java.util.ArrayList
-import java.util.Collections\rimport java.util.Arrays
 import org.opendaylight.yangtools.sal.binding.model.api.Restrictions
-import com.google.common.collect.Range
-import java.util.regex.Pattern
-import com.google.common.io.BaseEncoding
-import java.beans.ConstructorProperties
-import com.google.common.collect.Lists
 
 /**
- * Template for generating JAVA class. 
+ * Template for generating JAVA class.
  */
 class ClassTemplate extends BaseTemplate {
 
@@ -33,23 +37,22 @@ class ClassTemplate extends BaseTemplate {
     protected val List<GeneratedProperty> parentProperties
     protected val Iterable<GeneratedProperty> allProperties;
     protected val Restrictions restrictions
-    
+
     /**
      * List of enumeration which are generated as JAVA enum type.
      */
     protected val List<Enumeration> enums
-    
+
     /**
      * List of constant instances which are generated as JAVA public static final attributes.
      */
     protected val List<Constant> consts
-    
+
     /**
      * List of generated types which are enclosed inside <code>genType</code>
      */
     protected val List<GeneratedType> enclosedGeneratedTypes;
     
-    
     protected val GeneratedTransferObject genTO;
 
     /**
@@ -78,7 +81,6 @@ class ClassTemplate extends BaseTemplate {
         this.enclosedGeneratedTypes = genType.enclosedTypes
     }
 
-
     /**
      * Generates JAVA class source code (class body only).
      * 
@@ -88,7 +90,6 @@ class ClassTemplate extends BaseTemplate {
         return generateBody(true)
     }
 
-
     override protected body() {
         generateBody(false);
     }
@@ -107,9 +108,14 @@ class ClassTemplate extends BaseTemplate {
             Â«enumDeclarations»
             Â«constantsDeclarations»
             Â«generateFields»
-
-            Â«constructors»
             
+            Â«IF restrictions != null && (!restrictions.rangeConstraints.nullOrEmpty || 
+                !restrictions.lengthConstraints.nullOrEmpty)»
+            Â«generateConstraints»
+            
+            Â«ENDIF»
+            Â«constructors»
+
             Â«defaultInstance»
 
             Â«FOR field : properties SEPARATOR "\n"»
@@ -125,13 +131,30 @@ class ClassTemplate extends BaseTemplate {
 
             Â«generateToString(genTO.toStringIdentifiers)»
 
-            Â«generateLengthMethod("length", genTO, genTO.importedName, "_length")»
+            Â«generateLengthMethod("length", "_length")»
 
-            Â«generateRangeMethod("range", genTO.restrictions, genTO.importedName, "_range", allProperties)»
+            Â«generateRangeMethod("range", "_range")»
 
         }
     '''
 
+    def private generateLengthMethod(String methodName, String varName) '''
+        Â«IF restrictions != null && !(restrictions.lengthConstraints.empty)»
+            Â«val numberClass = restrictions.lengthConstraints.iterator.next.min.class»
+            public static Â«List.importedName»<«Range.importedName»<«numberClass.importedNumber»>> Â«methodName»() {
+                return Â«varName»;
+            }
+        Â«ENDIF»
+    '''
+
+    def private generateRangeMethod(String methodName, String varName) '''
+        Â«IF restrictions != null && !(restrictions.rangeConstraints.empty)»
+            Â«val returnType = allProperties.iterator.next.returnType»
+            public static Â«List.importedName»<«Range.importedName»<«returnType.importedNumber»>> Â«methodName»() {
+                return Â«varName»;
+            }
+        Â«ENDIF»
+    '''
 
     /**
      * Template method which generates inner classes inside this interface.
@@ -144,13 +167,12 @@ class ClassTemplate extends BaseTemplate {
                 Â«IF (innerClass instanceof GeneratedTransferObject)»
                     Â«val classTemplate = new ClassTemplate(innerClass as GeneratedTransferObject)»
                     Â«classTemplate.generateAsInnerClass»
-                    
+
                 Â«ENDIF»
             Â«ENDFOR»
         Â«ENDIF»
     '''
-    
-    
+
     def protected constructors() '''
         Â«IF genTO.unionType»
             Â«genUnionConstructor»
@@ -165,6 +187,55 @@ class ClassTemplate extends BaseTemplate {
         Â«ENDIF»
     '''
 
+    def private generateConstraints() '''
+        static {
+            Â«IF !restrictions.rangeConstraints.nullOrEmpty»
+            Â«generateRangeConstraints»
+            Â«ENDIF»
+            Â«IF !restrictions.lengthConstraints.nullOrEmpty»
+            Â«generateLengthConstraints»
+            Â«ENDIF»
+        }
+    '''
+
+    private def generateRangeConstraints() '''
+        Â«IF !allProperties.nullOrEmpty»
+            Â«val returnType = allProperties.iterator.next.returnType»
+            Â«IF returnType.fullyQualifiedName.equals(BigDecimal.canonicalName)»
+                Â«rangeBody(restrictions, BigDecimal, genTO.importedName, "_range")»
+            Â«ELSE»
+                Â«rangeBody(restrictions, BigInteger, genTO.importedName, "_range")»
+            Â«ENDIF»
+        Â«ENDIF»
+    '''
+
+    private def rangeBody(Restrictions restrictions, Class<? extends Number> numberClass, String className, String varName) '''
+        Â«ImmutableList.importedName».Builder<«Range.importedName»<«numberClass.importedName»>> builder = Â«ImmutableList.importedName».builder();
+        Â«FOR r : restrictions.rangeConstraints»
+            builder.add(«Range.importedName».closed(«numericValue(numberClass, r.min)», Â«numericValue(numberClass, r.max)»));
+        Â«ENDFOR»
+        Â«varName» = builder.build();
+    '''
+
+    private def lengthBody(Restrictions restrictions, Class<? extends Number> numberClass, String className, String varName) '''
+        Â«ImmutableList.importedName».Builder<«Range.importedName»<«numberClass.importedName»>> builder = Â«ImmutableList.importedName».builder();
+        Â«FOR r : restrictions.lengthConstraints»
+            builder.add(«Range.importedName».closed(«numericValue(numberClass, r.min)», Â«numericValue(numberClass, r.max)»));
+        Â«ENDFOR»
+        Â«varName» = builder.build();
+    '''
+
+    private def generateLengthConstraints() '''
+        Â«IF restrictions != null && !(restrictions.lengthConstraints.empty)»
+            Â«val numberClass = restrictions.lengthConstraints.iterator.next.min.class»
+            Â«IF numberClass.equals(typeof(BigDecimal))»
+                Â«lengthBody(restrictions, numberClass, genTO.importedName, "_length")»
+            Â«ELSE»
+                Â«lengthBody(restrictions, typeof(BigInteger), genTO.importedName, "_length")»
+            Â«ENDIF»
+        Â«ENDIF»
+    '''
+
     def protected allValuesConstructor() '''
     Â«IF genTO.typedef && !allProperties.empty && allProperties.size == 1 && allProperties.get(0).name.equals("value")»
         @«ConstructorProperties.importedName»("value")
@@ -180,6 +251,7 @@ class ClassTemplate extends BaseTemplate {
             this.«p.fieldName» = Â«p.fieldName»;
         Â«ENDFOR»
     }
+    
     '''
 
     def protected genUnionConstructor() '''
@@ -296,7 +368,7 @@ class ClassTemplate extends BaseTemplate {
             ENDFOR»«
         ENDIF
     Â»'''
-    
+
     /**
      * Template method which generates JAVA enum type.
      * 
@@ -369,10 +441,10 @@ class ClassTemplate extends BaseTemplate {
             Â«val prop = getPropByName("value")»
             Â«IF prop != null»
                 Â«IF !(restrictions.lengthConstraints.empty)»
-                    private static Â«List.importedName»<«Range.importedName»<«prop.returnType.importedNumber»>> _length;
+                    private static final Â«List.importedName»<«Range.importedName»<«prop.returnType.importedNumber»>> _length;
                 Â«ENDIF»
                 Â«IF !(restrictions.rangeConstraints.empty)»
-                    private static Â«List.importedName»<«Range.importedName»<«prop.returnType.importedNumber»>> _range;
+                    private static final Â«List.importedName»<«Range.importedName»<«prop.returnType.importedNumber»>> _range;
                 Â«ENDIF»
             Â«ENDIF»
         Â«ENDIF»
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/util/ClassLoaderUtils.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/util/ClassLoaderUtils.java
deleted file mode 100644 (file)
index e898e72..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * 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
- */
-package org.opendaylight.yangtools.concepts.util;
-
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
-import java.util.concurrent.Callable;
-import java.util.concurrent.locks.Lock;
-
-/**
- * @deprecated Use {@link org.opendaylight.yangtools.yang.binding.util.ClassLoaderUtils} instead.
- */
-@Deprecated
-public final class ClassLoaderUtils {
-
-    private ClassLoaderUtils() {
-        throw new UnsupportedOperationException("Utility class");
-    }
-
-    public static <V> V withClassLoader(final ClassLoader cls, final Callable<V> function) throws Exception {
-        return withClassLoaderAndLock(cls, null, function);
-    }
-
-    public static <V> V withClassLoaderAndLock(final ClassLoader cls, final Lock lock, final Callable<V> function) throws Exception {
-        if (cls == null) {
-            throw new IllegalArgumentException("Classloader should not be null");
-        }
-        if (function == null) {
-            throw new IllegalArgumentException("Function should not be null");
-        }
-
-        if (lock != null) {
-            lock.lock();
-        }
-        ClassLoader oldCls = Thread.currentThread().getContextClassLoader();
-        try {
-            Thread.currentThread().setContextClassLoader(cls);
-            return function.call();
-        } finally {
-            Thread.currentThread().setContextClassLoader(oldCls);
-            if (lock != null) {
-                lock.unlock();
-            }
-        }
-    }
-
-    public static ParameterizedType findParameterizedType(final Class<?> subclass, final Class<?> genericType) {
-        if(subclass == null || genericType == null) {
-            throw new IllegalArgumentException("Class was not specified.");
-        }
-        for (Type type : subclass.getGenericInterfaces()) {
-            if (type instanceof ParameterizedType && genericType.equals(((ParameterizedType) type).getRawType())) {
-                return (ParameterizedType) type;
-            }
-        }
-        return null;
-    }
-
-    public static <S,G,P> Class<P> findFirstGenericArgument(final Class<S> scannedClass, final Class<G> genericType) {
-        try {
-            return withClassLoader(scannedClass.getClassLoader(), ClassLoaderUtils.<S,G,P>findFirstGenericArgumentTask(scannedClass, genericType));
-        } catch (Exception e) {
-            return null;
-        }
-    }
-
-    private static <S,G,P> Callable<Class<P>> findFirstGenericArgumentTask(final Class<S> scannedClass, final Class<G> genericType) {
-        return new Callable<Class<P>>() {
-            @Override
-            @SuppressWarnings("unchecked")
-            public Class<P> call() throws Exception {
-                final ParameterizedType augmentationGeneric = findParameterizedType(scannedClass,
-                        genericType);
-                if (augmentationGeneric == null) {
-                    return null;
-                }
-                return (Class<P>) augmentationGeneric.getActualTypeArguments()[0];
-            }
-        };
-    }
-
-    public static Type getFirstGenericParameter(final Type type) {
-        if(type instanceof ParameterizedType) {
-            return ((ParameterizedType) type).getActualTypeArguments()[0];
-        }
-        return null;
-    }
-
-}
\ No newline at end of file
index e8f271c075360830e0fb3a5045ac0b47c0022956..07cc5ae6bed889c99ae38fad922e54a3801f886f 100644 (file)
@@ -12,10 +12,16 @@ import java.util.Collections;
 import java.util.EventListener;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+
 import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 
-
+/**
+ * @deprecated Use {@link org.opendaylight.yangtools.util.ListenerRegistry} instead.
+ *
+ * @param <T>
+ */
+@Deprecated
 public class ListenerRegistry<T extends EventListener> implements Iterable<ListenerRegistration<T>> {
 
     private final ConcurrentHashMap<ListenerRegistration<? extends T>,ListenerRegistration<? extends T>> listeners;
@@ -34,7 +40,7 @@ public class ListenerRegistry<T extends EventListener> implements Iterable<Liste
         return unmodifiableView;
     }
 
-    public ListenerRegistration<T> register(T listener) {
+    public ListenerRegistration<T> register(final T listener) {
         if (listener == null) {
             throw new IllegalArgumentException("Listener should not be null.");
         }
@@ -42,28 +48,26 @@ public class ListenerRegistry<T extends EventListener> implements Iterable<Liste
         listeners.put(ret,ret);
         return ret;
     }
-    
-    public <L extends T> ListenerRegistration<L> registerWithType(L listener) {
+
+    public <L extends T> ListenerRegistration<L> registerWithType(final L listener) {
         ListenerRegistrationImpl<L> ret = new ListenerRegistrationImpl<L>(listener);
         listeners.put(ret,ret);
         return ret;
     }
-    
+
     @Override
     public java.util.Iterator<ListenerRegistration<T>> iterator() {
         return unmodifiableView.iterator();
     }
 
     @SuppressWarnings("rawtypes")
-    private void remove(ListenerRegistrationImpl registration) {
+    private void remove(final ListenerRegistrationImpl registration) {
         listeners.remove(registration);
     }
 
-    private class ListenerRegistrationImpl<P extends EventListener> //
-            extends AbstractObjectRegistration<P> //
-            implements ListenerRegistration<P> {
+    private class ListenerRegistrationImpl<P extends EventListener> extends AbstractObjectRegistration<P> implements ListenerRegistration<P> {
 
-        public ListenerRegistrationImpl(P instance) {
+        public ListenerRegistrationImpl(final P instance) {
             super(instance);
         }
 
index 684d959f39fd5d8f417cfb7ff8ae3747c2816893..00c1cf4e880e8a7799bd549fd2a14cb317354ce9 100644 (file)
      </dependency>
      <dependency>
        <groupId>org.opendaylight.yangtools</groupId>
-       <artifactId>yang-data-json</artifactId>
+       <artifactId>yang-data-composite-node</artifactId>
      </dependency>
      <dependency>
        <groupId>org.opendaylight.yangtools</groupId>
index 84d4dfe9da7db6fc2d455dc6e66aeff0bdeb44d6..7c62febf18c631084dfd0e0778a4e60e2442e800 100644 (file)
             </dependency>
             <dependency>
                 <groupId>org.opendaylight.yangtools</groupId>
-                <artifactId>yang-data-json</artifactId>
+                <artifactId>yang-data-composite-node</artifactId>
                 <version>0.6.2-SNAPSHOT</version>
             </dependency>
             <dependency>
index d0ab7fc85e167ceda45d5d9b3a2474f6989c1415..5d185efe517345ac63c832837e7f3db06dd3e7c4 100644 (file)
             <groupId>org.opendaylight.yangtools</groupId>
             <artifactId>concepts</artifactId>
         </dependency>
-
+        <dependency>
+            <groupId>com.google.code.findbugs</groupId>
+            <artifactId>jsr305</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
             <artifactId>junit</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>mockito-configuration</artifactId>
+            <scope>test</scope>
+            <version>0.6.2-SNAPSHOT</version>
+        </dependency>
     </dependencies>
     <build>
         <plugins>
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/ClassLoaderUtils.java b/common/util/src/main/java/org/opendaylight/yangtools/util/ClassLoaderUtils.java
new file mode 100644 (file)
index 0000000..6f84ef8
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * 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 static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Supplier;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class ClassLoaderUtils {
+    private static final Logger LOG = LoggerFactory.getLogger(ClassLoaderUtils.class);
+
+    private ClassLoaderUtils() {
+        throw new UnsupportedOperationException("Utility class");
+    }
+
+    /**
+     *
+     * Runs {@link Supplier} with provided {@link ClassLoader}.
+     *
+     * Invokes supplies function and makes sure that original {@link ClassLoader}
+     * is context {@link ClassLoader} after execution.
+     *
+     * @param cls {@link ClassLoader} to be used.
+     * @param function Function to be executed.
+     * @return Result of supplier invocation.
+     *
+     */
+    public static <V> V withClassLoader(final ClassLoader cls, final Supplier<V> function) {
+        checkNotNull(cls, "Classloader should not be null");
+        checkNotNull(function, "Function should not be null");
+
+        final ClassLoader oldCls = Thread.currentThread().getContextClassLoader();
+        try {
+            Thread.currentThread().setContextClassLoader(cls);
+            return function.get();
+        } finally {
+            Thread.currentThread().setContextClassLoader(oldCls);
+        }
+    }
+
+    /**
+     *
+     * Runs {@link Callable} with provided {@link ClassLoader}.
+     *
+     * Invokes supplies function and makes sure that original {@link ClassLoader}
+     * is context {@link ClassLoader} after execution.
+     *
+     * @param cls {@link ClassLoader} to be used.
+     * @param function Function to be executed.
+     * @return Result of callable invocation.
+     *
+     */
+    public static <V> V withClassLoader(final ClassLoader cls, final Callable<V> function) throws Exception {
+        checkNotNull(cls, "Classloader should not be null");
+        checkNotNull(function, "Function should not be null");
+
+        final ClassLoader oldCls = Thread.currentThread().getContextClassLoader();
+        try {
+            Thread.currentThread().setContextClassLoader(cls);
+            return function.call();
+        } finally {
+            Thread.currentThread().setContextClassLoader(oldCls);
+        }
+    }
+
+    public static Object construct(final Constructor<? extends Object> constructor, final List<Object> objects)
+            throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+        final Object[] initargs = objects.toArray();
+        return constructor.newInstance(initargs);
+    }
+
+    /**
+     *
+     * Loads class using this supplied classloader.
+     *
+     *
+     * @param cls
+     * @param name String name of class.
+     * @return
+     * @throws ClassNotFoundException
+     */
+    public static Class<?> loadClass(final ClassLoader cls, final String name) throws ClassNotFoundException {
+        if ("byte[]".equals(name)) {
+            return byte[].class;
+        }
+        if ("char[]".equals(name)) {
+            return char[].class;
+        }
+
+        try {
+            return cls.loadClass(name);
+        } catch (ClassNotFoundException e) {
+            String[] components = name.split("\\.");
+            String potentialOuter;
+            int length = components.length;
+            if (length > 2 && (potentialOuter = components[length - 2]) != null && Character.isUpperCase(potentialOuter.charAt(0))) {
+                String outerName = Joiner.on(".").join(Arrays.asList(components).subList(0, length - 1));
+                String innerName = outerName + "$" + components[length-1];
+                return cls.loadClass(innerName);
+            } else {
+                throw e;
+            }
+        }
+    }
+
+    public static Class<?> loadClassWithTCCL(final String name) throws ClassNotFoundException {
+        return loadClass(Thread.currentThread().getContextClassLoader(), name);
+    }
+
+    public static Class<?> tryToLoadClassWithTCCL(final String fullyQualifiedName) {
+        try {
+            return loadClassWithTCCL(fullyQualifiedName);
+        } catch (ClassNotFoundException e) {
+            LOG.debug("Failed to load class {}", fullyQualifiedName, e);
+            return null;
+        }
+    }
+
+    public static <S,G,P> Class<P> findFirstGenericArgument(final Class<S> scannedClass, final Class<G> genericType) {
+        return withClassLoader(scannedClass.getClassLoader(), ClassLoaderUtils.<S,G,P>findFirstGenericArgumentTask(scannedClass, genericType));
+    }
+
+    private static <S,G,P> Supplier<Class<P>> findFirstGenericArgumentTask(final Class<S> scannedClass, final Class<G> genericType) {
+        return new Supplier<Class<P>>() {
+            @Override
+            @SuppressWarnings("unchecked")
+            public Class<P> get() {
+                final ParameterizedType augmentationGeneric = findParameterizedType(scannedClass, genericType);
+                if (augmentationGeneric != null) {
+                    return (Class<P>) augmentationGeneric.getActualTypeArguments()[0];
+                }
+                return null;
+            }
+        };
+    }
+
+    public static ParameterizedType findParameterizedType(final Class<?> subclass, final Class<?> genericType) {
+        Preconditions.checkNotNull(subclass);
+        Preconditions.checkNotNull(genericType);
+
+        for (Type type : subclass.getGenericInterfaces()) {
+            if (type instanceof ParameterizedType && genericType.equals(((ParameterizedType) type).getRawType())) {
+                return (ParameterizedType) type;
+            }
+        }
+
+        LOG.debug("Class {} does not declare interface {}", subclass, genericType);
+        return null;
+    }
+
+    public static Type getFirstGenericParameter(final Type type) {
+        if (type instanceof ParameterizedType) {
+            return ((ParameterizedType) type).getActualTypeArguments()[0];
+        }
+        return null;
+    }
+}
index 36f729fb8ff69210e475ad67f8466f3dd973f5f0..51a8d16f84ce007c3499b8f9e04e78766c310c6e 100644 (file)
@@ -12,6 +12,7 @@ import java.util.concurrent.ExecutorService;
 import java.util.concurrent.RejectedExecutionException;
 import java.util.concurrent.RejectedExecutionHandler;
 import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -25,11 +26,15 @@ public final class ExecutorServiceUtil {
     private static final class WaitInQueueExecutionHandler implements RejectedExecutionHandler {
         @Override
         public void rejectedExecution(final Runnable r, final ThreadPoolExecutor executor) {
+            if( executor.isShutdown() ) {
+                throw new RejectedExecutionException( "Executor has been shutdown." );
+            }
+
             try {
                 executor.getQueue().put(r);
             } catch (InterruptedException e) {
-                LOG.debug("Intterupted while waiting for queue", e);
-                throw new RejectedExecutionException("Interrupted while waiting for queue", e);
+                LOG.debug("Interrupted while attempting to put to the queue", e);
+                throw new RejectedExecutionException("Interrupted while attempting to put to the queue", e);
             }
         }
     }
@@ -42,7 +47,7 @@ public final class ExecutorServiceUtil {
     }
 
     /**
-     * Create a {@link BlockingQueue} which does not allow for non-blocking addition to the queue.
+     * Creates a {@link BlockingQueue} which does not allow for non-blocking addition to the queue.
      * This is useful with {@link #waitInQueueExecutionHandler()} to turn force a
      * {@link ThreadPoolExecutor} to create as many threads as it is configured to before starting
      * to fill the queue.
@@ -50,7 +55,7 @@ public final class ExecutorServiceUtil {
      * @param delegate Backing blocking queue.
      * @return A new blocking queue backed by the delegate
      */
-    public <E> BlockingQueue<E> offerFailingBlockingQueue(final BlockingQueue<E> delegate) {
+    public static <E> BlockingQueue<E> offerFailingBlockingQueue(final BlockingQueue<E> delegate) {
         return new ForwardingBlockingQueue<E>() {
             @Override
             public boolean offer(final E o) {
@@ -65,12 +70,31 @@ public final class ExecutorServiceUtil {
     }
 
     /**
-     * Return a {@link RejectedExecutionHandler} which blocks on the {@link ThreadPoolExecutor}'s
+     * Returns a {@link RejectedExecutionHandler} which blocks on the {@link ThreadPoolExecutor}'s
      * backing queue if a new thread cannot be spawned.
      *
      * @return A shared RejectedExecutionHandler instance.
      */
-    public RejectedExecutionHandler waitInQueueExecutionHandler() {
+    public static RejectedExecutionHandler waitInQueueExecutionHandler() {
         return WAIT_IN_QUEUE_HANDLER;
     }
+
+    /**
+     * Tries to shutdown the given executor gracefully by awaiting termination for the given
+     * timeout period. If the timeout elapses before termination, the executor is forcefully
+     * shutdown.
+     */
+    public static void tryGracefulShutdown(final ExecutorService executor, long timeout,
+            TimeUnit unit ) {
+
+        executor.shutdown();
+
+        try {
+            if(!executor.awaitTermination(timeout, unit)) {
+                executor.shutdownNow();
+            }
+        } catch( InterruptedException e ) {
+            executor.shutdownNow();
+        }
+    }
 }
similarity index 97%
rename from common/concepts/src/main/java/org/opendaylight/yangtools/concepts/util/Immutables.java
rename to common/util/src/main/java/org/opendaylight/yangtools/util/Immutables.java
index cc2d0186d4a03c5e7af8eaca829c3d278dc6a2cd..e10977b6de01ecd0c2254a0fd1c263f4088e8aac 100644 (file)
@@ -5,7 +5,7 @@
  * 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.concepts.util;
+package org.opendaylight.yangtools.util;
 
 import java.math.BigDecimal;
 import java.math.BigInteger;
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/LazyCollections.java b/common/util/src/main/java/org/opendaylight/yangtools/util/LazyCollections.java
new file mode 100644 (file)
index 0000000..c70fd8c
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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
+ */
+package org.opendaylight.yangtools.util;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Utility methods for lazily instantiated collections. These are useful for
+ * situations when we start off with an empty collection (where Collections.empty()
+ * can be reused), but need to add more things.
+ */
+public final class LazyCollections {
+
+    /**
+     * Add an element to a list, potentially transforming the list.
+     *
+     * @param list Current list
+     * @param obj Object that needs to be added
+     * @return new list
+     */
+    public static <T> List<T> lazyAdd(final List<T> list, final T obj) {
+        final List<T> ret;
+
+        switch (list.size()) {
+        case 0:
+            return Collections.singletonList(obj);
+        case 1:
+            ret = new ArrayList<>();
+            ret.addAll(list);
+            break;
+        default:
+            ret = list;
+        }
+
+        ret.add(obj);
+        return ret;
+    }
+
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/ListenerRegistry.java b/common/util/src/main/java/org/opendaylight/yangtools/util/ListenerRegistry.java
new file mode 100644 (file)
index 0000000..e8b1a3d
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * 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
+ */
+package org.opendaylight.yangtools.util;
+
+
+import java.util.Collections;
+import java.util.EventListener;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+
+
+public class ListenerRegistry<T extends EventListener> implements Iterable<ListenerRegistration<T>> {
+
+    private final ConcurrentHashMap<ListenerRegistration<? extends T>,ListenerRegistration<? extends T>> listeners;
+    final Set<ListenerRegistration<T>> unmodifiableView;
+
+    @SuppressWarnings("unchecked")
+    public ListenerRegistry() {
+        listeners = new ConcurrentHashMap<>();
+        // This conversion is known to be safe.
+        @SuppressWarnings("rawtypes")
+        final Set rawSet = Collections.unmodifiableSet(listeners.keySet());
+        unmodifiableView = rawSet;
+    }
+
+    public Iterable<ListenerRegistration<T>> getListeners() {
+        return unmodifiableView;
+    }
+
+    public ListenerRegistration<T> register(T listener) {
+        if (listener == null) {
+            throw new IllegalArgumentException("Listener should not be null.");
+        }
+        ListenerRegistrationImpl<T> ret = new ListenerRegistrationImpl<T>(listener);
+        listeners.put(ret,ret);
+        return ret;
+    }
+    
+    public <L extends T> ListenerRegistration<L> registerWithType(L listener) {
+        ListenerRegistrationImpl<L> ret = new ListenerRegistrationImpl<L>(listener);
+        listeners.put(ret,ret);
+        return ret;
+    }
+    
+    @Override
+    public java.util.Iterator<ListenerRegistration<T>> iterator() {
+        return unmodifiableView.iterator();
+    }
+
+    @SuppressWarnings("rawtypes")
+    private void remove(ListenerRegistrationImpl registration) {
+        listeners.remove(registration);
+    }
+
+    private class ListenerRegistrationImpl<P extends EventListener> //
+            extends AbstractObjectRegistration<P> //
+            implements ListenerRegistration<P> {
+
+        public ListenerRegistrationImpl(P instance) {
+            super(instance);
+        }
+
+        @Override
+        protected void removeRegistration() {
+            ListenerRegistry.this.remove(this);
+        }
+    }
+
+    public static <T extends EventListener> ListenerRegistry<T> create() {
+        return new ListenerRegistry<>();
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/PropertyUtils.java b/common/util/src/main/java/org/opendaylight/yangtools/util/PropertyUtils.java
new file mode 100644 (file)
index 0000000..cc8f94d
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * 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 org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Strings;
+
+/**
+ * Provides utilities for system properties.
+ *
+ * @author Thomas Pantelis
+ */
+public final class PropertyUtils {
+
+    private static final Logger LOG = LoggerFactory.getLogger(PropertyUtils.class);
+
+    private PropertyUtils() {
+    }
+
+    /**
+     * Obtains the given property from the System properties and returns as an int. If the property
+     * is not found the specified default value is returned. If the property value can't be parsed
+     * to an int, a warning is logged and the default value is returned.
+     *
+     * @param propName the name of the property to get
+     * @param defaultValue the default value
+     * @return the System property as an int or the <code>defaultValue</code> if not found.
+     */
+    public static int getIntSystemProperty( String propName, int defaultValue ) {
+        int propValue = defaultValue;
+        String strValue = System.getProperty(propName);
+        if (!Strings.isNullOrEmpty(strValue) && !strValue.trim().isEmpty() ) {
+            try {
+                propValue = Integer.parseInt(strValue);
+            } catch (NumberFormatException e) {
+                LOG.warn("Cannot parse value {} for system property {}, using default {}",
+                         strValue, propName, defaultValue);
+            }
+        }
+
+        return propValue;
+    }
+}
index 88da121c8616f44aba44d633d9e599e1396fc5a4..4d429f1aaa11c899f6759cea472d48dabde75171 100644 (file)
@@ -27,7 +27,7 @@ final class ReadOnlyTrieMap<K, V> extends ForwardingMap<K, V> {
     private static final Logger LOG = LoggerFactory.getLogger(ReadOnlyTrieMap.class);
     private final TrieMap<K, V> readWrite;
     private final int size;
-    private TrieMap<K, V> readOnly;
+    private volatile TrieMap<K, V> readOnly;
 
     ReadOnlyTrieMap(final TrieMap<K, V> map, final int size) {
         super();
@@ -43,15 +43,18 @@ final class ReadOnlyTrieMap<K, V> extends ForwardingMap<K, V> {
 
     @Override
     protected Map<K, V> delegate() {
-        if (readOnly == null) {
+        TrieMap<K, V> ret = readOnly;
+        if (ret == null) {
             synchronized (this) {
-                if (readOnly == null) {
-                    readOnly = readWrite.readOnlySnapshot();
+                ret = readOnly;
+                if (ret == null) {
+                    ret = readWrite.readOnlySnapshot();
+                    readOnly = ret;
                 }
             }
         }
 
-        return readOnly;
+        return ret;
     }
 
     @Override
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/AsyncNotifyingListenableFutureTask.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/AsyncNotifyingListenableFutureTask.java
new file mode 100644 (file)
index 0000000..69c94f3
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * 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.concurrent;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executor;
+import java.util.concurrent.FutureTask;
+
+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
+ * allows an {@link Executor} to be specified on construction that is used to execute listener
+ * callback Runnables, registered via {@link #addListener}, asynchronously when this task completes.
+ * This is useful when you want to guarantee listener executions are off-loaded onto another thread
+ * to avoid blocking the thread that completed this task, as a common use case is to pass an
+ * executor that runs tasks in the same thread as the caller (ie MoreExecutors#sameThreadExecutor)
+ * to {@link #addListener}.
+ * <p>
+ * Note: the Executor specified on construction does not replace the Executor specified in
+ * {@link #addListener}. The latter Executor is still used however, if it is detected that the
+ * listener Runnable would execute in the thread that completed this task, the listener
+ * is executed on Executor specified on construction.
+ *
+ * @author Thomas Pantelis
+ *
+ * @param <V> the Future result value type
+ */
+public class AsyncNotifyingListenableFutureTask<V> extends FutureTask<V> implements ListenableFuture<V> {
+
+    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<>();
+
+    /**
+     *  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( Runnable runnable, @Nullable V result,
+            @Nullable Executor listenerExecutor ) {
+        super( runnable, result );
+        this.listenerExecutor = listenerExecutor;
+    }
+
+    /**
+     * Creates an {@code AsyncListenableFutureTask} that will upon running, execute the given
+     * {@code Callable}.
+     *
+     * @param callable the callable task
+     * @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 );
+    }
+
+    /**
+     * Creates a {@code AsyncListenableFutureTask} that will upon running, execute the
+     * given {@code Runnable}, and arrange that {@code get} will return the
+     * given result on successful completion.
+     *
+     * @param runnable the runnable task
+     * @param result the result to return on successful completion.
+     * @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 );
+    }
+
+    @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 );
+    }
+
+    /**
+     * Called by the base class when the future result is set. We invoke our listeners.
+     */
+    @Override
+    protected void done() {
+        ON_TASK_COMPLETION_THREAD_TL.set( Boolean.TRUE );
+        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 );
+            }
+        }
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/AsyncNotifyingListeningExecutorService.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/AsyncNotifyingListeningExecutorService.java
new file mode 100644 (file)
index 0000000..ef4670d
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * 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.concurrent;
+
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.Nullable;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.AbstractListeningExecutorService;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * An {@link ListeningExecutorService} implementation that also allows for an {@link Executor} to be
+ * specified on construction that is used to execute {@link ListenableFuture} callback Runnables,
+ * registered via {@link Futures#addCallback} or {@link ListenableFuture#addListener} directly,
+ * asynchronously when a task that is run on this executor completes. This is useful when you want
+ * to guarantee listener callback executions are off-loaded onto another thread to avoid blocking
+ * the thread that completed the task, as a common use case is to pass an executor that runs tasks
+ * in the same thread as the caller (ie <code>MoreExecutors#sameThreadExecutor</code>}) to
+ * {@link ListenableFuture#addListener}.
+ * <p>
+ * Most commonly, this class would be used in lieu of <code>MoreExecutors#listeningDecorator<code>
+ * when the underlying delegate Executor is single-threaded, in which case, you may not want
+ * ListenableFuture callbacks to block the single thread.
+ * <p>
+ * Note: the Executor specified on construction does not replace the Executor specified in
+ * {@link ListenableFuture#addListener}. The latter Executor is still used however, if it is
+ * detected that the listener Runnable would execute in the thread that completed the task, the
+ * listener is executed on Executor specified on construction.
+ *
+ * @author Thomas Pantelis
+ * @see AsyncNotifyingListenableFutureTask
+ */
+public class AsyncNotifyingListeningExecutorService extends AbstractListeningExecutorService {
+
+    private final ExecutorService delegate;
+    private final Executor listenableFutureExecutor;
+
+    /**
+     * Constructor.
+     *
+     * @param delegate the back-end ExecutorService.
+     * @param listenableFutureExecutor the executor used to run listener callbacks asynchronously.
+     *     If null, no executor is used.
+     */
+    public AsyncNotifyingListeningExecutorService( ExecutorService delegate,
+            @Nullable Executor listenableFutureExecutor ) {
+        this.delegate = Preconditions.checkNotNull( delegate );
+        this.listenableFutureExecutor = listenableFutureExecutor;
+    }
+
+    /**
+     * Creates an {@link AsyncNotifyingListenableFutureTask} instance with the listener Executor.
+     *
+     * @param task the Callable to execute
+     */
+    private <T> AsyncNotifyingListenableFutureTask<T> newFutureTask( Callable<T> task ) {
+        return AsyncNotifyingListenableFutureTask.create( task, listenableFutureExecutor );
+    }
+
+    /**
+     * Creates an {@link AsyncNotifyingListenableFutureTask} instance with the listener Executor.
+     *
+     * @param task the Runnable to execute
+     */
+    private <T> AsyncNotifyingListenableFutureTask<T> newFutureTask( Runnable task, T result ) {
+        return AsyncNotifyingListenableFutureTask.create( task, result, listenableFutureExecutor );
+    }
+
+    /**
+     * Returns the delegate ExecutorService.
+     */
+    protected ExecutorService getDelegate() {
+        return delegate;
+    }
+
+    @Override
+    public boolean awaitTermination( long timeout, TimeUnit unit ) throws InterruptedException {
+        return delegate.awaitTermination( timeout, unit );
+    }
+
+    @Override
+    public boolean isShutdown() {
+        return delegate.isShutdown();
+    }
+
+    @Override
+    public boolean isTerminated() {
+        return delegate.isTerminated();
+    }
+
+    @Override
+    public void shutdown() {
+        delegate.shutdown();
+    }
+
+    @Override
+    public List<Runnable> shutdownNow() {
+        return delegate.shutdownNow();
+    }
+
+    @Override
+    public void execute( Runnable command ) {
+        delegate.execute( command );
+    }
+
+    @Override
+    public <T> ListenableFuture<T> submit( Callable<T> task ) {
+        AsyncNotifyingListenableFutureTask<T> futureTask = newFutureTask( task );
+        delegate.execute( futureTask );
+        return futureTask;
+    }
+
+    @Override
+    public ListenableFuture<?> submit( Runnable task ) {
+        AsyncNotifyingListenableFutureTask<Void> futureTask = newFutureTask( task, null );
+        delegate.execute( futureTask );
+        return futureTask;
+    }
+
+    @Override
+    public <T> ListenableFuture<T> submit( Runnable task, T result ) {
+        AsyncNotifyingListenableFutureTask<T> futureTask = newFutureTask( task, result );
+        delegate.execute( futureTask );
+        return futureTask;
+    }
+
+    protected ToStringHelper addToStringAttributes( ToStringHelper toStringHelper ) {
+        return toStringHelper;
+    }
+
+    @Override
+    public final String toString(){
+        return addToStringAttributes( Objects.toStringHelper( this )
+                .add( "delegate", delegate ) ).toString();
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/CachedThreadPoolExecutor.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/CachedThreadPoolExecutor.java
new file mode 100644 (file)
index 0000000..4936efa
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * 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.concurrent;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+/**
+ * A ThreadPoolExecutor with a specified bounded queue capacity that favors reusing previously
+ * constructed threads, when they are available, over creating new threads.
+ * <p>
+ * See {@link SpecialExecutors#newBoundedCachedThreadPool} for more details.
+ *
+ * @author Thomas Pantelis
+ */
+public class CachedThreadPoolExecutor extends ThreadPoolExecutor {
+
+    private static final long IDLE_TIMEOUT_IN_SEC = 60L;
+
+    private final AtomicLong largestBackingQueueSize = new AtomicLong( 0 );
+
+    private final ExecutorQueue executorQueue;
+
+    private final String threadPrefix;
+
+    private final int maximumQueueSize;
+
+    private final RejectedTaskHandler rejectedTaskHandler;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param maximumPoolSize
+     *            the maximum number of threads to allow in the pool. Threads will terminate after
+     *            being idle for 60 seconds.
+     * @param maximumQueueSize
+     *            the capacity of the queue.
+     * @param threadPrefix
+     *            the name prefix for threads created by this executor.
+     */
+    public CachedThreadPoolExecutor( int maximumPoolSize, int maximumQueueSize, String threadPrefix ) {
+        // We're using a custom SynchronousQueue that has a backing bounded LinkedBlockingQueue.
+        // We don't specify any core threads (first parameter) so, when a task is submitted,
+        // the base class will always try to offer to the queue. If there is an existing waiting
+        // thread, the offer will succeed and the task will be handed to the thread to execute. If
+        // there's no waiting thread, either because there are no threads in the pool or all threads
+        // are busy, the base class will try to create a new thread. If the maximum thread limit has
+        // been reached, the task will be rejected. We specify a RejectedTaskHandler that tries
+        // to offer to the backing queue. If that succeeds, the task will execute as soon as a
+        // thread becomes available. If the offer fails to the backing queue, the task is rejected.
+        super( 0, maximumPoolSize, IDLE_TIMEOUT_IN_SEC, TimeUnit.SECONDS,
+               new ExecutorQueue( maximumQueueSize ) );
+
+        this.threadPrefix = Preconditions.checkNotNull( threadPrefix );
+        this.maximumQueueSize = maximumQueueSize;
+
+        setThreadFactory( new ThreadFactoryBuilder().setDaemon( true )
+                                            .setNameFormat( this.threadPrefix + "-%d" ).build() );
+
+        executorQueue = (ExecutorQueue)super.getQueue();
+
+        rejectedTaskHandler = new RejectedTaskHandler(
+                executorQueue.getBackingQueue(), largestBackingQueueSize );
+        super.setRejectedExecutionHandler( rejectedTaskHandler );
+    }
+
+    @Override
+    public void setRejectedExecutionHandler( RejectedExecutionHandler handler ) {
+        rejectedTaskHandler.setDelegateRejectedExecutionHandler( handler );
+    }
+
+    @Override
+    public BlockingQueue<Runnable> getQueue(){
+        return executorQueue.getBackingQueue();
+    }
+
+    public long getLargestQueueSize() {
+        return largestBackingQueueSize.get();
+    }
+
+    protected ToStringHelper addToStringAttributes( ToStringHelper toStringHelper ) {
+        return toStringHelper;
+    }
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes( Objects.toStringHelper( this )
+                .add( "Thread Prefix", threadPrefix )
+                .add( "Current Thread Pool Size", getPoolSize() )
+                .add( "Largest Thread Pool Size", getLargestPoolSize() )
+                .add( "Max Thread Pool Size", getMaximumPoolSize() )
+                .add( "Current Queue Size", executorQueue.getBackingQueue().size() )
+                .add( "Largest Queue Size", getLargestQueueSize() )
+                .add( "Max Queue Size", maximumQueueSize )
+                .add( "Active Thread Count", getActiveCount() )
+                .add( "Completed Task Count", getCompletedTaskCount() )
+                .add( "Total Task Count", getTaskCount() ) ).toString();
+    }
+
+    /**
+     * A customized SynchronousQueue that has a backing bounded LinkedBlockingQueue. This class
+     * overrides the #poll methods to first try to poll the backing queue for a task. If the backing
+     * queue is empty, it calls the base SynchronousQueue#poll method. In this manner, we get the
+     * thread reuse behavior of the SynchronousQueue with the added ability to queue tasks when all
+     * threads are busy.
+     */
+    private static class ExecutorQueue extends SynchronousQueue<Runnable> {
+
+        private static final long serialVersionUID = 1L;
+
+        private static final long POLL_WAIT_TIME_IN_MS = 300;
+
+        private final LinkedBlockingQueue<Runnable> backingQueue;
+
+        ExecutorQueue( int maxBackingQueueSize ) {
+            backingQueue = new LinkedBlockingQueue<>( maxBackingQueueSize );
+        }
+
+        LinkedBlockingQueue<Runnable> getBackingQueue() {
+            return backingQueue;
+        }
+
+        @Override
+        public Runnable poll( long timeout, TimeUnit unit ) throws InterruptedException {
+            long totalWaitTime = unit.toMillis( timeout );
+            long waitTime = Math.min( totalWaitTime, POLL_WAIT_TIME_IN_MS );
+            Runnable task = null;
+
+            // We loop here, each time polling the backingQueue first then our queue, instead of
+            // polling each just once. This is to handle the following timing edge case:
+            //
+            //   We poll the backingQueue and it's empty but, before the call to super.poll,
+            //   a task is offered but no thread is immediately available and the task is put on the
+            //   backingQueue. There is a slight chance that all the other threads could be at the
+            //   same point, in which case they would all call super.poll and wait. If we only
+            //   called poll once, no thread would execute the task (unless/until another task was
+            //   later submitted). But by looping and breaking the specified timeout into small
+            //   periods, one thread will eventually wake up and get the task from the backingQueue
+            //   and execute it, although slightly delayed.
+
+            while( task == null ) {
+                // First try to get a task from the backing queue.
+                task = backingQueue.poll();
+                if( task == null ) {
+                    // No task in backing - call the base class to wait for one to be offered.
+                    task = super.poll( waitTime, TimeUnit.MILLISECONDS );
+
+                    totalWaitTime -= POLL_WAIT_TIME_IN_MS;
+                    if( totalWaitTime <= 0 ) {
+                        break;
+                    }
+
+                    waitTime = Math.min( totalWaitTime, POLL_WAIT_TIME_IN_MS );
+                }
+            }
+
+            return task;
+        }
+
+        @Override
+        public Runnable poll() {
+            Runnable task = backingQueue.poll();
+            return task != null ? task : super.poll();
+        }
+    }
+
+    /**
+     * Internal RejectedExecutionHandler that tries to offer rejected tasks to the backing queue.
+     * If the queue is full, we throw a RejectedExecutionException by default. The client can
+     * override this behavior be specifying their own RejectedExecutionHandler, in which case we
+     * delegate to that handler.
+     */
+    private static class RejectedTaskHandler implements RejectedExecutionHandler {
+
+        private final LinkedBlockingQueue<Runnable> backingQueue;
+        private final AtomicLong largestBackingQueueSize;
+        private volatile RejectedExecutionHandler delegateRejectedExecutionHandler;
+
+        RejectedTaskHandler( LinkedBlockingQueue<Runnable> backingQueue,
+                             AtomicLong largestBackingQueueSize ) {
+            this.backingQueue = backingQueue;
+            this.largestBackingQueueSize = largestBackingQueueSize;
+        }
+
+        void setDelegateRejectedExecutionHandler(
+                RejectedExecutionHandler delegateRejectedExecutionHandler ){
+            this.delegateRejectedExecutionHandler = delegateRejectedExecutionHandler;
+        }
+
+        @Override
+        public void rejectedExecution( Runnable task, ThreadPoolExecutor executor ) {
+            if( executor.isShutdown() ) {
+                throw new RejectedExecutionException( "Executor has been shutdown." );
+            }
+
+            if( !backingQueue.offer( task ) ) {
+                if( delegateRejectedExecutionHandler != null ) {
+                    delegateRejectedExecutionHandler.rejectedExecution( task, executor );
+                } else {
+                    throw new RejectedExecutionException(
+                                                "All threads are in use and the queue is full" );
+                }
+            }
+
+            largestBackingQueueSize.incrementAndGet();
+            long size = backingQueue.size();
+            long largest = largestBackingQueueSize.get();
+            if( size > largest ) {
+                largestBackingQueueSize.compareAndSet( largest, size );
+            }
+        }
+    }
+}
index 39332be85e092b9c5567316a2e5ac0eb84a07ddb..011872d6b138d9edbaf28869524121bd859f0b43 100644 (file)
@@ -11,18 +11,17 @@ package org.opendaylight.yangtools.util.concurrent;
 import static com.google.common.base.Preconditions.checkNotNull;
 
 import com.google.common.base.Function;
-import com.google.common.util.concurrent.AbstractListeningExecutorService;
 import com.google.common.util.concurrent.ForwardingListenableFuture;
 import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.ListenableFutureTask;
-
-import java.util.List;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
+import javax.annotation.Nullable;
+
 /**
  * An implementation of ListeningExecutorService that attempts to detect deadlock scenarios that
  * could occur if clients invoke the returned Future's <ode>get</code> methods synchronously.
@@ -45,10 +44,9 @@ import java.util.concurrent.TimeoutException;
  *
  * @author Thomas Pantelis
  */
-public class DeadlockDetectingListeningExecutorService extends AbstractListeningExecutorService {
+public class DeadlockDetectingListeningExecutorService extends AsyncNotifyingListeningExecutorService {
     private final ThreadLocal<Boolean> deadlockDetector = new ThreadLocal<>();
     private final Function<Void, Exception> deadlockExceptionFunction;
-    private final ExecutorService delegate;
 
     /**
      * Constructor.
@@ -57,61 +55,45 @@ public class DeadlockDetectingListeningExecutorService extends AbstractListening
      * @param deadlockExceptionFunction Function that returns an Exception instance to set as the
      *             cause of the ExecutionException when a deadlock is detected.
      */
-    public DeadlockDetectingListeningExecutorService(final ExecutorService delegate,
-            final Function<Void,Exception> deadlockExceptionFunction) {
-        this.delegate = checkNotNull(delegate);
-        this.deadlockExceptionFunction = checkNotNull(deadlockExceptionFunction);
-    }
-
-    @Override
-    public boolean awaitTermination(final long timeout, final TimeUnit unit) throws InterruptedException {
-        return delegate.awaitTermination(timeout, unit);
-    }
-
-    @Override
-    public boolean isShutdown() {
-        return delegate.isShutdown();
-    }
-
-    @Override
-    public boolean isTerminated() {
-        return delegate.isTerminated();
+    public DeadlockDetectingListeningExecutorService( ExecutorService delegate,
+                                          Function<Void,Exception> deadlockExceptionFunction ) {
+        this(delegate, deadlockExceptionFunction, null);
     }
 
-    @Override
-    public void shutdown() {
-        delegate.shutdown();
-    }
-
-    @Override
-    public List<Runnable> shutdownNow() {
-        return delegate.shutdownNow();
+    /**
+     * Constructor.
+     *
+     * @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.
+     * @param listenableFutureExecutor the executor used to run listener callbacks asynchronously.
+     *             If null, no executor is used.
+     */
+    public DeadlockDetectingListeningExecutorService( ExecutorService delegate,
+                                          Function<Void,Exception> deadlockExceptionFunction,
+                                          @Nullable Executor listenableFutureExecutor ) {
+        super(delegate, listenableFutureExecutor);
+        this.deadlockExceptionFunction = checkNotNull(deadlockExceptionFunction);
     }
 
     @Override
-    public void execute(final Runnable command) {
-        delegate.execute(wrapRunnable(command));
+    public void execute( Runnable command ){
+        getDelegate().execute(wrapRunnable(command));
     }
 
     @Override
-    public <T> ListenableFuture<T> submit(final Callable<T> task ) {
-        final ListenableFutureTask<T> futureTask = ListenableFutureTask.create(wrapCallable(task));
-        delegate.execute(futureTask);
-        return wrapListenableFuture(futureTask);
+    public <T> ListenableFuture<T> submit( Callable<T> task ){
+        return wrapListenableFuture(super.submit(wrapCallable(task)));
     }
 
     @Override
-    public ListenableFuture<?> submit( final Runnable task ) {
-        ListenableFutureTask<Void> futureTask = ListenableFutureTask.create(wrapRunnable(task), null);
-        delegate.execute(futureTask);
-        return wrapListenableFuture(futureTask);
+    public ListenableFuture<?> submit( Runnable task ){
+        return wrapListenableFuture(super.submit(wrapRunnable(task)));
     }
 
     @Override
-    public <T> ListenableFuture<T> submit(final Runnable task, final T result) {
-        ListenableFutureTask<T> futureTask = ListenableFutureTask.create(wrapRunnable(task), result);
-        delegate.execute(futureTask);
-        return wrapListenableFuture(futureTask);
+    public <T> ListenableFuture<T> submit( Runnable task, T result ){
+        return wrapListenableFuture(super.submit(wrapRunnable(task), result));
     }
 
     private Runnable wrapRunnable(final Runnable task) {
@@ -122,7 +104,7 @@ public class DeadlockDetectingListeningExecutorService extends AbstractListening
                 try {
                     task.run();
                 } finally {
-                    deadlockDetector.set(null);
+                    deadlockDetector.remove();
                 }
             }
         };
@@ -136,7 +118,7 @@ public class DeadlockDetectingListeningExecutorService extends AbstractListening
                 try {
                     return delagate.call();
                 } finally {
-                    deadlockDetector.set(null);
+                    deadlockDetector.remove();
                 }
             }
         };
@@ -144,11 +126,12 @@ public class DeadlockDetectingListeningExecutorService extends AbstractListening
 
     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 so, we detect this as
-         * a deadlock and throw an ExecutionException even though it may not be a deadlock if there are
-         * more than 1 thread in the pool. Either way, there's bad practice somewhere, either on the client
-         * side for doing a blocking call or in the framework's threading model.
+         * 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
+         * so, we detect this as a deadlock and throw an ExecutionException even though it may not
+         * be a deadlock if there are more than 1 thread in the pool. Either way, there's bad
+         * practice somewhere, either on the client side for doing a blocking call or in the
+         * framework's threading model.
          */
         return new ForwardingListenableFuture.SimpleForwardingListenableFuture<T>(delegate) {
             @Override
index af51032dd27b5b65dedcd96b2a62c9f0c32f8fb7..604ca3692c6f41b3cc74f941a1f16e201ca591a2 100644 (file)
@@ -43,6 +43,15 @@ public abstract class ExceptionMapper<X extends Exception> implements Function<E
         this.opName = Preconditions.checkNotNull(opName);
     }
 
+    /**
+     * Return the exception class produced by this instance.
+     *
+     * @return Exception class.
+     */
+    protected final Class<X> getExceptionType() {
+        return exceptionType;
+    }
+
     /**
      * Invoked to create a new exception instance of the specified type.
      *
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/FastThreadPoolExecutor.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/FastThreadPoolExecutor.java
new file mode 100644 (file)
index 0000000..b7549eb
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * 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.concurrent;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+/**
+ * A ThreadPoolExecutor with a specified bounded queue capacity that favors creating new threads
+ * over queuing, as the former is faster.
+ * <p>
+ * See {@link SpecialExecutors#newFastBlockingThreadPool} for more details.
+ *
+ * @author Thomas Pantelis
+ */
+public class FastThreadPoolExecutor extends ThreadPoolExecutor {
+
+    private static final long DEFAULT_IDLE_TIMEOUT_IN_SEC = 15L;
+
+    private final String threadPrefix;
+    private final int maximumQueueSize;
+
+    /**
+     * Constructs a FastThreadPoolExecutor instance.
+     *
+     * @param maximumPoolSize
+     *            the maximum number of threads to allow in the pool. Threads will terminate after
+     *            being idle for 15 seconds.
+     * @param maximumQueueSize
+     *            the capacity of the queue.
+     * @param threadPrefix
+     *            the name prefix for threads created by this executor.
+     */
+    public FastThreadPoolExecutor( int maximumPoolSize, int maximumQueueSize, String threadPrefix ) {
+        this( maximumPoolSize, maximumQueueSize, DEFAULT_IDLE_TIMEOUT_IN_SEC, TimeUnit.SECONDS,
+              threadPrefix );
+    }
+
+    /**
+     * Constructs a FastThreadPoolExecutor instance.
+     *
+     * @param maximumPoolSize
+     *            the maximum number of threads to allow in the pool.
+     * @param maximumQueueSize
+     *            the capacity of the queue.
+     * @param keepAliveTime
+     *            the maximum time that idle threads will wait for new tasks before terminating.
+     * @param unit
+     *            the time unit for the keepAliveTime argument
+     * @param threadPrefix
+     *            the name prefix for threads created by this executor.
+     */
+    public FastThreadPoolExecutor( int maximumPoolSize, int maximumQueueSize, long keepAliveTime,
+            TimeUnit unit, String threadPrefix ) {
+        // We use all core threads (the first 2 parameters below equal) so, when a task is submitted,
+        // if the thread limit hasn't been reached, a new thread will be spawned to execute
+        // the task even if there is an existing idle thread in the pool. This is faster than
+        // handing the task to an existing idle thread via the queue. Once the thread limit is
+        // reached, subsequent tasks will be queued. If the queue is full, tasks will be rejected.
+
+        super( maximumPoolSize, maximumPoolSize, keepAliveTime, unit,
+               new LinkedBlockingQueue<Runnable>( maximumQueueSize ) );
+
+        this.threadPrefix = threadPrefix;
+        this.maximumQueueSize = maximumQueueSize;
+
+        setThreadFactory( new ThreadFactoryBuilder().setDaemon( true )
+                                                 .setNameFormat( threadPrefix + "-%d" ).build() );
+
+        if( keepAliveTime > 0 ) {
+            // Need to specifically configure core threads to timeout.
+            allowCoreThreadTimeOut( true );
+        }
+    }
+
+    protected ToStringHelper addToStringAttributes( ToStringHelper toStringHelper ) {
+        return toStringHelper;
+    }
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes( Objects.toStringHelper( this )
+                .add( "Thread Prefix", threadPrefix )
+                .add( "Current Thread Pool Size", getPoolSize() )
+                .add( "Largest Thread Pool Size", getLargestPoolSize() )
+                .add( "Max Thread Pool Size", getMaximumPoolSize() )
+                .add( "Current Queue Size", getQueue().size() )
+                .add( "Max Queue Size", maximumQueueSize )
+                .add( "Active Thread Count", getActiveCount() )
+                .add( "Completed Task Count", getCompletedTaskCount() )
+                .add( "Total Task Count", getTaskCount() ) ).toString();
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/NotificationManager.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/NotificationManager.java
new file mode 100644 (file)
index 0000000..41cc7dc
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * 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.concurrent;
+
+import java.util.concurrent.RejectedExecutionException;
+
+/**
+ * Interface for a class that manages queuing and dispatching notifications for multiple listeners.
+ *
+ * @author Thomas Pantelis
+ *
+ * @param <L> the listener type
+ * @param <N> the notification type
+ */
+public interface NotificationManager<L, N> {
+
+    /**
+     * Submits a notification to be queued and dispatched to the given listener.
+     * <p>
+     * <b>Note:</b> This method may block if the listener queue is currently full.
+     *
+     * @param listener the listener to notify
+     * @param notification the notification to dispatch
+     * @throws RejectedExecutionException if the notification can't be queued for dispatching
+     */
+    void submitNotification( L listener, N notification )
+            throws RejectedExecutionException;
+
+    /**
+     * Submits notifications to be queued and dispatched to the given listener.
+     * <p>
+     * <b>Note:</b> This method may block if the listener queue is currently full.
+     *
+     * @param listener the listener to notify
+     * @param notifications the notifications to dispatch
+     * @throws RejectedExecutionException if a notification can't be queued for dispatching
+     */
+    void submitNotifications( L listener, Iterable<N> notifications )
+            throws RejectedExecutionException;
+
+}
\ No newline at end of file
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/QueuedNotificationManager.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/QueuedNotificationManager.java
new file mode 100644 (file)
index 0000000..472520d
--- /dev/null
@@ -0,0 +1,389 @@
+/*
+ * 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.concurrent;
+
+import java.util.Arrays;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.Executor;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import javax.annotation.concurrent.GuardedBy;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * This class manages queuing and dispatching notifications for multiple listeners concurrently.
+ * Notifications are queued on a per-listener basis and dispatched serially to each listener via an
+ * {@link Executor}.
+ * <p>
+ * This class optimizes its memory footprint by only allocating and maintaining a queue and executor
+ * task for a listener when there are pending notifications. On the first notification(s), a queue
+ * is created and a task is submitted to the executor to dispatch the queue to the associated
+ * listener. Any subsequent notifications that occur before all previous notifications have been
+ * dispatched are appended to the existing queue. When all notifications have been dispatched, the
+ * queue and task are discarded.
+ *
+ * @author Thomas Pantelis
+ *
+ * @param <L> the listener type
+ * @param <N> the notification type
+ */
+public class QueuedNotificationManager<L,N> implements NotificationManager<L,N> {
+
+    /**
+     * Interface implemented by clients that does the work of invoking listeners with notifications.
+     *
+     * @author Thomas Pantelis
+     *
+     * @param <L> the listener type
+     * @param <N> the notification type
+     */
+    public interface Invoker<L,N> {
+
+        /**
+         * Called to invoke a listener with a notification.
+         *
+         * @param listener the listener to invoke
+         * @param notification the notification to send
+         */
+        void invokeListener( L listener, N notification );
+    }
+
+    private static final Logger LOG = LoggerFactory.getLogger( QueuedNotificationManager.class );
+
+    private final Executor executor;
+    private final Invoker<L,N> listenerInvoker;
+
+    private final ConcurrentMap<ListenerKey<L>,NotificationTask>
+                                                          listenerCache = new ConcurrentHashMap<>();
+
+    private final String name;
+    private final int maxQueueCapacity;
+
+    /**
+     * Constructor.
+     *
+     * @param executor the {@link Executor} to use for notification tasks
+     * @param listenerInvoker the {@link Invoker} to use for invoking listeners
+     * @param maxQueueCapacity the capacity of each listener queue
+     * @param name the name of this instance for logging info
+     */
+    public QueuedNotificationManager( Executor executor, Invoker<L,N> listenerInvoker,
+            int maxQueueCapacity, String name ) {
+        this.executor = Preconditions.checkNotNull( executor );
+        this.listenerInvoker = Preconditions.checkNotNull( listenerInvoker );
+        Preconditions.checkArgument( maxQueueCapacity > 0, "maxQueueCapacity must be > 0 " );
+        this.maxQueueCapacity = maxQueueCapacity;
+        this.name = Preconditions.checkNotNull( name );
+    }
+
+    /* (non-Javadoc)
+     * @see org.opendaylight.yangtools.util.concurrent.NotificationManager#addNotification(L, N)
+     */
+    @Override
+    public void submitNotification( final L listener, final N notification )
+            throws RejectedExecutionException {
+
+        if( notification == null ) {
+            return;
+        }
+
+        submitNotifications( listener, Arrays.asList( notification ) );
+    }
+
+    /* (non-Javadoc)
+     * @see org.opendaylight.yangtools.util.concurrent.NotificationManager#submitNotifications(L, java.util.Collection)
+     */
+    @Override
+    public void submitNotifications( final L listener, final Iterable<N> notifications )
+            throws RejectedExecutionException {
+
+        if( notifications == null || listener == null ) {
+            return;
+        }
+
+        if( LOG.isTraceEnabled() ) {
+            LOG.trace( "{}: submitNotifications for listener {}: {}",
+                       name, listener.getClass(), notifications );
+        }
+
+        ListenerKey<L> key = new ListenerKey<>( listener );
+        NotificationTask newNotificationTask = null;
+
+        // Keep looping until we are either able to add a new NotificationTask or are able to
+        // add our notifications to an existing NotificationTask. Eventually one or the other
+        // will occur.
+
+        try {
+            while( true ) {
+                NotificationTask existingTask = listenerCache.get( key );
+
+                if( existingTask == null || !existingTask.submitNotifications( notifications ) ) {
+
+                    // Either there's no existing task or we couldn't add our notifications to the
+                    // existing one because it's in the process of exiting and removing itself from
+                    // the cache. Either way try to put a new task in the cache. If we can't put
+                    // then either the existing one is still there and hasn't removed itself quite
+                    // yet or some other concurrent thread beat us to the put although this method
+                    // shouldn't be called concurrently for the same listener as that would violate
+                    // notification ordering. In any case loop back up and try again.
+
+                    if( newNotificationTask == null ) {
+                        newNotificationTask = new NotificationTask( key, notifications );
+                    }
+
+                    existingTask = listenerCache.putIfAbsent( key, newNotificationTask );
+                    if( existingTask == null ) {
+
+                        // We were able to put our new task - now submit it to the executor and
+                        // we're done. If it throws a RejectedxecutionException, let that propagate
+                        // to the caller.
+
+                        LOG.debug( "{}: Submitting NotificationTask for listener {}",
+                                   name, listener.getClass() );
+
+                        executor.execute( newNotificationTask );
+                        break;
+                    }
+                } else {
+
+                    // We were able to add our notifications to an existing task so we're done.
+
+                    break;
+                }
+            }
+        } catch( InterruptedException e ) {
+
+            // We were interrupted trying to offer to the listener's queue. Somebody's probably
+            // telling us to quit.
+
+            LOG.debug( "{}: Interrupted trying to add to {} listener's queue",
+                       name, listener.getClass() );
+        }
+
+        if( LOG.isTraceEnabled() ) {
+            LOG.trace( "{}: submitNotifications dine for listener {}",
+                       name, listener.getClass() );
+        }
+    }
+
+    /**
+     * Used as the listenerCache map key. We key by listener reference identity hashCode/equals.
+     * Since we don't know anything about the listener class implementations and we're mixing
+     * multiple listener class instances in the same map, this avoids any potential issue with an
+     * equals implementation that just blindly casts the other Object to compare instead of checking
+     * for instanceof.
+     */
+    private static class ListenerKey<L> {
+
+        private final L listener;
+
+        public ListenerKey( L listener ) {
+            this.listener = listener;
+        }
+
+        L getListener() {
+            return listener;
+        }
+
+        @Override
+        public int hashCode() {
+            return System.identityHashCode( listener );
+        }
+
+        @Override
+        public boolean equals( Object obj ) {
+            ListenerKey<?> other = (ListenerKey<?>) obj;
+            return listener == other.listener;
+        }
+    }
+
+    /**
+     * Executor task for a single listener that queues notifications and sends them serially to the
+     * listener.
+     */
+    private class NotificationTask implements Runnable {
+
+        private final BlockingQueue<N> notificationQueue;
+
+        private volatile boolean done = false;
+
+        @GuardedBy("queuingLock")
+        private boolean queuedNotifications = false;
+
+        private final Lock queuingLock = new ReentrantLock();
+
+        private final ListenerKey<L> listenerKey;
+
+        NotificationTask( ListenerKey<L> listenerKey, Iterable<N> notifications ) {
+
+            this.listenerKey = listenerKey;
+            this.notificationQueue = new LinkedBlockingQueue<>( maxQueueCapacity );
+
+            for( N notification: notifications ) {
+                this.notificationQueue.add( notification );
+            }
+        }
+
+        boolean submitNotifications( Iterable<N> notifications ) throws InterruptedException {
+
+            queuingLock.lock();
+            try {
+
+                // Check the done flag - if true then #run is in the process of exiting so return
+                // false to indicate such. Otherwise, offer the notifications to the queue.
+
+                if( done ) {
+                    return false;
+                }
+
+                for( N notification: notifications ) {
+
+                    while( true ) {
+
+                        // Try to offer for up to a minute and log a message if it times out.
+
+                        // FIXME: we loop forever to guarantee delivery however this leaves it open
+                        // for 1 rogue listener to bring everyone to a halt. Another option is to
+                        // limit the tries and give up after a while and drop the notification.
+                        // Given a reasonably large queue capacity and long timeout, if we still
+                        // can't queue then most likely the listener is an unrecoverable state
+                        // (deadlock or endless loop).
+
+                        if( LOG.isDebugEnabled() ) {
+                            LOG.debug( "{}: Offering notification to the queue for listener {}: {}",
+                                       name, listenerKey.getListener().getClass(), notification );
+                        }
+
+                        if( notificationQueue.offer( notification, 1, TimeUnit.MINUTES ) ) {
+                            break;
+                        }
+
+                        LOG.warn(
+                            "{}: Timed out trying to offer a notification to the queue for listener {}." +
+                            "The queue has reached its capacity of {}",
+                            name, listenerKey.getListener().getClass(), maxQueueCapacity );
+                    }
+                }
+
+                // Set the queuedNotifications flag to tell #run that we've just queued
+                // notifications and not to exit yet, even if it thinks the queue is empty at this
+                // point.
+
+                queuedNotifications = true;
+
+            } finally {
+                queuingLock.unlock();
+            }
+
+            return true;
+        }
+
+        @Override
+        public void run() {
+
+            try {
+                // Loop until we've dispatched all the notifications in the queue.
+
+                while( true ) {
+
+                    // Get the notification at the head of the queue, waiting a little bit for one
+                    // to get offered.
+
+                    N notification = notificationQueue.poll( 10, TimeUnit.MILLISECONDS );
+                    if( notification == null ) {
+
+                        // The queue is empty - try to get the queuingLock. If we can't get the lock
+                        // then #submitNotifications is in the process of offering to the queue so
+                        // we'll loop back up and poll the queue again.
+
+                        if( queuingLock.tryLock() ) {
+                            try {
+
+                                // Check the queuedNotifications flag to see if #submitNotifications
+                                // has offered new notification(s) to the queue. If so, loop back up
+                                // and poll the queue again. Otherwise set done to true and exit.
+                                // Once we set the done flag and unlock, calls to
+                                // #submitNotifications will fail and a new task will be created.
+
+                                if( !queuedNotifications ) {
+                                    done = true;
+                                    break;
+                                }
+
+                                // Clear the queuedNotifications flag so we'll try to exit the next
+                                // time through the loop when the queue is empty.
+
+                                queuedNotifications = false;
+
+                            } finally {
+                                queuingLock.unlock();
+                            }
+                        }
+                    }
+
+                    notifyListener( notification );
+                }
+            } catch( InterruptedException e ) {
+
+                // The executor is probably shutting down so log as debug.
+                LOG.debug( "{}: Interrupted trying to remove from {} listener's queue",
+                           name, listenerKey.getListener().getClass() );
+            } finally {
+
+                // We're exiting, gracefully or not - either way make sure we always remove
+                // ourselves from the cache.
+
+                listenerCache.remove( listenerKey );
+            }
+        }
+
+        private void notifyListener( N notification ) {
+
+            if( notification == null ) {
+                return;
+            }
+
+            try {
+
+                if( LOG.isDebugEnabled() ) {
+                    LOG.debug( "{}: Invoking listener {} with notification: {}",
+                               name, listenerKey.getListener().getClass(), notification );
+                }
+
+                listenerInvoker.invokeListener( listenerKey.getListener(), notification );
+
+            } catch( RuntimeException e ) {
+
+                // We'll let a RuntimeException from the listener slide and keep sending any
+                // remaining notifications.
+
+                LOG.error( String.format( "%1$s: Error notifying listener %2$s", name,
+                           listenerKey.getListener().getClass() ), e );
+
+            } catch( Error e ) {
+
+                // A JVM Error is severe - best practice is to throw them up the chain. Set done to
+                // true so no new notifications can be added to this task as we're about to bail.
+
+                done = true;
+                throw e;
+            }
+        }
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/ReflectiveExceptionMapper.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/ReflectiveExceptionMapper.java
new file mode 100644 (file)
index 0000000..3eb8c13
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2014 Robert Varga.  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;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * Convenience {@link ExceptionMapper} which instantiates specified Exception using
+ * reflection. The Exception types are expected to declare an accessible constructor
+ * which takes two arguments: a String and a Throwable.
+ *
+ * @param <X> Exception type
+ */
+public final class ReflectiveExceptionMapper<X extends Exception> extends ExceptionMapper<X> {
+    private final Constructor<X> ctor;
+
+    private ReflectiveExceptionMapper(final String opName, final Constructor<X> ctor) {
+        super(opName, ctor.getDeclaringClass());
+        this.ctor = ctor;
+    }
+
+    @Override
+    protected X newWithCause(final String message, final Throwable cause) {
+        try {
+            return ctor.newInstance(message, cause);
+        } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+            throw new IllegalStateException("Failed to instantiate exception " + ctor.getDeclaringClass(), e);
+        }
+    }
+
+    /**
+     * Create a new instance of the reflective exception mapper. This method performs basic
+     * sanity checking on the exception class. This method is potentially very costly, so
+     * users are strongly encouraged to cache the returned mapper for reuse.
+     *
+     * @param opName Operation performed
+     * @param exceptionType Exception type
+     * @return A new mapper instance
+     * @throws IllegalArgumentException when the supplied exception class does not pass sanity checks
+     * @throws SecurityException when the required constructor is not accessible
+     */
+    public static <X extends Exception> ReflectiveExceptionMapper<X> create(final String opName, final Class<X> exceptionType) throws SecurityException {
+        final Constructor<X> c;
+        try {
+            c = exceptionType.getConstructor(String.class, Throwable.class);
+        } catch (NoSuchMethodException e) {
+            throw new IllegalArgumentException("Class does not define a String, Throwable constructor", e);
+        }
+
+        try {
+            c.newInstance(opName, new Throwable());
+        } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+            throw new IllegalArgumentException("Constructor " + c.getName() + " failed to pass instantiation test", e);
+        }
+
+        return new ReflectiveExceptionMapper<>(opName, c);
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/SpecialExecutors.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/SpecialExecutors.java
new file mode 100644 (file)
index 0000000..0548d7a
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * 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.concurrent;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Factory methods for creating {@link ExecutorService} instances with specific configurations.
+
+ * @author Thomas Pantelis
+ */
+public final class SpecialExecutors {
+
+    private SpecialExecutors() {
+    }
+
+    /**
+     * Creates an ExecutorService with a specified bounded queue capacity that favors creating new
+     * threads over queuing, as the former is faster, so threads will only be reused when the thread
+     * limit is exceeded and tasks are queued. If the maximum queue capacity is reached, subsequent
+     * tasks will be rejected.
+     * <p>
+     * For example, if the maximum number of threads is 100 and 100 short-lived tasks are submitted
+     * within say 10 seconds, then 100 threads will be created and used - previously constructed
+     * idle threads will not be reused. This provides the fastest execution of the 100 tasks at the
+     * expense of memory and thread resource overhead. Therefore it is advisable to specify a
+     * relatively small thread limit (probably no more than 50).
+     * <p>
+     * Threads that have not been used for 15 seconds are terminated and removed from the pool.
+     * Thus, a pool that remains idle for long enough will not consume any resources.
+     * <p>
+     * If you need an executor with less memory and thread resource overhead where slower execution
+     * time is acceptable, consider using {@link #newBoundedCachedThreadPool }.
+     *
+     * @param maximumPoolSize
+     *            the maximum number of threads to allow in the pool. Threads will terminate after
+     *            being idle for 15 seconds.
+     * @param maximumQueueSize
+     *            the capacity of the queue.
+     * @param threadPrefix
+     *            the name prefix for threads created by this executor.
+     * @return a new ExecutorService with the specified configuration.
+     */
+    public static ExecutorService newBoundedFastThreadPool( int maximumPoolSize,
+            int maximumQueueSize, String threadPrefix ) {
+        return new FastThreadPoolExecutor( maximumPoolSize, maximumQueueSize, threadPrefix );
+    }
+
+    /**
+     * Creates an ExecutorService similar to {@link #newBoundedFastThreadPool } except that it
+     * handles rejected tasks by running them in the same thread as the caller. Therefore if the
+     * queue is full, the caller submitting the task will be blocked until the task completes. In
+     * this manner, tasks are never rejected.
+     *
+     * @param maximumPoolSize
+     *            the maximum number of threads to allow in the pool. Threads will terminate after
+     *            being idle for 15 seconds.
+     * @param maximumQueueSize
+     *            the capacity of the queue.
+     * @param threadPrefix
+     *            the name prefix for threads created by this executor.
+     * @return a new ExecutorService with the specified configuration.
+     */
+    public static ExecutorService newBlockingBoundedFastThreadPool( int maximumPoolSize,
+            int maximumQueueSize, String threadPrefix ) {
+
+        FastThreadPoolExecutor executor =
+                new FastThreadPoolExecutor( maximumPoolSize, maximumQueueSize, threadPrefix );
+        executor.setRejectedExecutionHandler( new ThreadPoolExecutor.CallerRunsPolicy() );
+        return executor;
+    }
+
+    /**
+     * Creates an ExecutorService with a specified bounded queue capacity that favors reusing
+     * previously constructed threads, when they are available, over creating new threads. When a
+     * task is submitted, if no existing thread is available, a new thread will be created and added
+     * to the pool. If there is an existing idle thread available, the task will be handed to that
+     * thread to execute. If the specified maximum thread limit is reached, subsequent tasks will be
+     * queued and will execute as threads become available. If the maximum queue capacity is
+     * reached, subsequent tasks will be rejected.
+     * <p>
+     * Threads that have not been used for sixty seconds are terminated and removed from the pool.
+     * Thus, a pool that remains idle for long enough will not consume any resources.
+     * <p>
+     * By reusing threads when possible, this executor optimizes for reduced memory and thread
+     * resource overhead at the expense of execution time.
+     * <p>
+     * If you need an executor with faster execution time where increased memory and thread resource
+     * overhead is acceptable, consider using {@link #newBoundedFastThreadPool }.
+     *
+     * @param maximumPoolSize
+     *            the maximum number of threads to allow in the pool. Threads will terminate after
+     *            being idle for 60 seconds.
+     * @param maximumQueueSize
+     *            the capacity of the queue.
+     * @param threadPrefix
+     *            the name prefix for threads created by this executor.
+     * @return a new ExecutorService with the specified configuration.
+     */
+    public static ExecutorService newBoundedCachedThreadPool( int maximumPoolSize,
+            int maximumQueueSize, String threadPrefix ) {
+        return new CachedThreadPoolExecutor( maximumPoolSize, maximumQueueSize, threadPrefix );
+    }
+
+    /**
+     * Creates an ExecutorService similar to {@link #newBoundedCachedThreadPool } except that it
+     * handles rejected tasks by running them in the same thread as the caller. Therefore if the
+     * queue is full, the caller submitting the task will be blocked until the task completes. In
+     * this manner, tasks are never rejected.
+     *
+     * @param maximumPoolSize
+     *            the maximum number of threads to allow in the pool. Threads will terminate after
+     *            being idle for 60 seconds.
+     * @param maximumQueueSize
+     *            the capacity of the queue.
+     * @param threadPrefix
+     *            the name prefix for threads created by this executor.
+     * @return a new ExecutorService with the specified configuration.
+     */
+    public static ExecutorService newBlockingBoundedCachedThreadPool( int maximumPoolSize,
+            int maximumQueueSize, String threadPrefix ) {
+
+        CachedThreadPoolExecutor executor =
+                new CachedThreadPoolExecutor( maximumPoolSize, maximumQueueSize, threadPrefix );
+        executor.setRejectedExecutionHandler( new ThreadPoolExecutor.CallerRunsPolicy() );
+        return executor;
+    }
+
+    /**
+     * Creates an ExecutorService that uses a single worker thread operating off a bounded queue
+     * with the specified capacity. Tasks are guaranteed to execute sequentially, and no more than
+     * one task will be active at any given time. If the maximum queue capacity is reached,
+     * subsequent tasks will be rejected.
+     *
+     * @param maximumQueueSize
+     *            the capacity of the queue.
+     * @param threadPrefix
+     *            the name prefix for the thread created by this executor.
+     * @return a new ExecutorService with the specified configuration.
+     */
+    public static ExecutorService newBoundedSingleThreadExecutor( int maximumQueueSize,
+            String threadPrefix ) {
+        return new FastThreadPoolExecutor( 1, maximumQueueSize, Long.MAX_VALUE, TimeUnit.SECONDS,
+                threadPrefix );
+    }
+}
diff --git a/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/AsyncNotifyingListeningExecutorServiceTest.java b/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/AsyncNotifyingListeningExecutorServiceTest.java
new file mode 100644 (file)
index 0000000..1a3bf0d
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * 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.concurrent;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.junit.After;
+import org.junit.Test;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+import static org.opendaylight.yangtools.util.concurrent.CommonTestUtils.Invoker;
+import static org.opendaylight.yangtools.util.concurrent.CommonTestUtils.SUBMIT_CALLABLE;
+import static org.opendaylight.yangtools.util.concurrent.CommonTestUtils.SUBMIT_RUNNABLE;
+import static org.opendaylight.yangtools.util.concurrent.CommonTestUtils.SUBMIT_RUNNABLE_WITH_RESULT;
+
+/**
+ * Unit tests for AsyncNotifyingListeningExecutorService.
+ *
+ * @author Thomas Pantelis
+ */
+public class AsyncNotifyingListeningExecutorServiceTest {
+
+    private ExecutorService listenerExecutor;
+    private AsyncNotifyingListeningExecutorService testExecutor;
+
+    @After
+    public void tearDown() {
+        if( listenerExecutor != null ) {
+            listenerExecutor.shutdownNow();
+        }
+
+        if( testExecutor != null ) {
+            testExecutor.shutdownNow();
+        }
+    }
+
+    @Test
+    public void testListenerCallbackWithExecutor() throws InterruptedException {
+
+        String listenerThreadPrefix = "ListenerThread";
+        listenerExecutor = Executors.newFixedThreadPool( 3,
+                new ThreadFactoryBuilder().setNameFormat( listenerThreadPrefix + "-%d" ).build() );
+
+        testExecutor = new AsyncNotifyingListeningExecutorService(
+                Executors.newSingleThreadExecutor(
+                        new ThreadFactoryBuilder().setNameFormat( "SingleThread" ).build() ),
+                listenerExecutor );
+
+        testListenerCallback( testExecutor, SUBMIT_CALLABLE, listenerThreadPrefix );
+        testListenerCallback( testExecutor, SUBMIT_RUNNABLE, listenerThreadPrefix );
+        testListenerCallback( testExecutor, SUBMIT_RUNNABLE_WITH_RESULT, listenerThreadPrefix );
+    }
+
+    @Test
+    public void testListenerCallbackWithNoExecutor() throws InterruptedException {
+
+        String listenerThreadPrefix = "SingleThread";
+        testExecutor = new AsyncNotifyingListeningExecutorService(
+                Executors.newSingleThreadExecutor(
+                        new ThreadFactoryBuilder().setNameFormat( listenerThreadPrefix ).build() ),
+                null );
+
+        testListenerCallback( testExecutor, SUBMIT_CALLABLE, listenerThreadPrefix );
+        testListenerCallback( testExecutor, SUBMIT_RUNNABLE, listenerThreadPrefix );
+        testListenerCallback( testExecutor, SUBMIT_RUNNABLE_WITH_RESULT, listenerThreadPrefix );
+    }
+
+    static void testListenerCallback( AsyncNotifyingListeningExecutorService executor,
+            Invoker invoker, final String expListenerThreadPrefix ) throws InterruptedException {
+
+        AtomicReference<AssertionError> assertError = new AtomicReference<>();
+        CountDownLatch futureNotifiedLatch = new CountDownLatch( 1 );
+        CountDownLatch blockTaskLatch = new CountDownLatch( 1 );
+
+        // The blockTaskLatch is used to block the task from completing until we've added
+        // our listener to the Future. Otherwise, if the task completes quickly and the Future is
+        // set to done before we've added our listener, the call to ListenableFuture#addListener
+        // will immediately notify synchronously on this thread as Futures#addCallback defaults to
+        // a same thread executor. This would erroneously fail the test.
+
+        ListenableFuture<?> future = invoker.invokeExecutor( executor, blockTaskLatch );
+        addCallback( future, futureNotifiedLatch, expListenerThreadPrefix, assertError );
+
+        // Now that we've added our listener, signal the latch to let the task complete.
+
+        blockTaskLatch.countDown();
+
+        assertTrue( "ListenableFuture callback was not notified of onSuccess",
+                    futureNotifiedLatch.await( 5, TimeUnit.SECONDS ) );
+
+        if( assertError.get() != null ) {
+            throw assertError.get();
+        }
+
+        // Add another listener - since the Future is already complete, we expect the listener to be
+        // notified inline on this thread when it's added.
+
+        futureNotifiedLatch = new CountDownLatch( 1 );
+        addCallback( future, futureNotifiedLatch, Thread.currentThread().getName(), assertError );
+
+        assertTrue( "ListenableFuture callback was not notified of onSuccess",
+                    futureNotifiedLatch.await( 5, TimeUnit.SECONDS ) );
+
+        if( assertError.get() != null ) {
+            throw assertError.get();
+        }
+    }
+
+    static void addCallback( ListenableFuture<?> future,
+            final CountDownLatch futureNotifiedLatch,
+            final String expListenerThreadPrefix,
+            final AtomicReference<AssertionError> assertError ) {
+
+        Futures.addCallback( future, new FutureCallback<Object>() {
+            @Override
+            public void onSuccess( Object result ) {
+
+                try {
+                    String theadName = Thread.currentThread().getName();
+                    assertTrue( "ListenableFuture callback was not notified on the listener executor."
+                        + " Expected thread name prefix \"" + expListenerThreadPrefix +
+                            "\". Actual thread name \"" + theadName + "\"",
+                            theadName.startsWith( expListenerThreadPrefix ) );
+                } catch( AssertionError e ) {
+                    assertError.set( e );
+                } finally {
+                    futureNotifiedLatch.countDown();
+                }
+            }
+
+            @Override
+            public void onFailure( Throwable t ) {
+                // Shouldn't happen
+                t.printStackTrace();
+            }
+        } );
+    }
+
+    @Test
+    public void testDelegatedMethods() throws InterruptedException {
+
+        Runnable task = new Runnable() {
+            @Override
+            public void run(){
+            }
+        };
+
+        List<Runnable> taskList = Lists.newArrayList();
+
+        ExecutorService mockDelegate = mock( ExecutorService.class );
+        doNothing().when( mockDelegate ).execute( task );
+        doNothing().when( mockDelegate ).shutdown();
+        doReturn( taskList ).when( mockDelegate ).shutdownNow();
+        doReturn( true ).when( mockDelegate ).awaitTermination( 3, TimeUnit.SECONDS );
+        doReturn( true ).when( mockDelegate ).isShutdown();
+        doReturn( true ).when( mockDelegate ).isTerminated();
+
+        AsyncNotifyingListeningExecutorService executor = new AsyncNotifyingListeningExecutorService(
+                                                                   mockDelegate, null );
+
+        executor.execute( task );
+        executor.shutdown();
+        assertEquals( "awaitTermination", true, executor.awaitTermination( 3, TimeUnit.SECONDS ) );
+        assertSame( "shutdownNow", taskList, executor.shutdownNow() );
+        assertEquals( "isShutdown", true, executor.isShutdown() );
+        assertEquals( "isTerminated", true, executor.isTerminated() );
+
+        verify( mockDelegate ).execute( task );
+        verify( mockDelegate ).shutdown();
+        verify( mockDelegate ).awaitTermination( 3, TimeUnit.SECONDS );
+        verify( mockDelegate ).shutdownNow();
+        verify( mockDelegate ).isShutdown();
+        verify( mockDelegate ).isTerminated();
+    }
+}
diff --git a/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/CommonTestUtils.java b/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/CommonTestUtils.java
new file mode 100644 (file)
index 0000000..60c56a4
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * 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.concurrent;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.Uninterruptibles;
+
+/**
+ * Some common test utilities.
+ *
+ * @author Thomas Pantelis
+ */
+public class CommonTestUtils {
+
+    public interface Invoker {
+        ListenableFuture<?> invokeExecutor( ListeningExecutorService executor,
+                CountDownLatch blockingLatch );
+    };
+
+    public static final Invoker SUBMIT_CALLABLE = new Invoker() {
+        @Override
+        public ListenableFuture<?> invokeExecutor( ListeningExecutorService executor,
+                final CountDownLatch blockingLatch ) {
+            return executor.submit( new Callable<Void>() {
+                @Override
+                public Void call() throws Exception {
+                    if( blockingLatch != null ) {
+                        Uninterruptibles.awaitUninterruptibly( blockingLatch );
+                    }
+                    return null;
+                }
+            } );
+        }
+    };
+
+    public static final Invoker SUBMIT_RUNNABLE =  new Invoker() {
+        @Override
+        public ListenableFuture<?> invokeExecutor( ListeningExecutorService executor,
+                final CountDownLatch blockingLatch ) {
+            return executor.submit( new Runnable() {
+                @Override
+                public void run() {
+                    if( blockingLatch != null ) {
+                        Uninterruptibles.awaitUninterruptibly( blockingLatch );
+                    }
+                }
+            } );
+        }
+    };
+
+    public static final Invoker SUBMIT_RUNNABLE_WITH_RESULT = new Invoker() {
+        @Override
+        public ListenableFuture<?> invokeExecutor( ListeningExecutorService executor,
+                final CountDownLatch blockingLatch ) {
+            return executor.submit( new Runnable() {
+                @Override
+                public void run() {
+                    if( blockingLatch != null ) {
+                        Uninterruptibles.awaitUninterruptibly( blockingLatch );
+                    }
+                }
+            }, "foo" );
+        }
+    };
+}
index b23750da986f94f6c08a392558a6924f50a2ef7d..6bba351dcabb28a56a7c3cf434c32f29b63adfae 100644 (file)
@@ -13,10 +13,12 @@ import static org.junit.Assert.*;
 import java.util.concurrent.Callable;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -25,6 +27,13 @@ import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+import static org.opendaylight.yangtools.util.concurrent.AsyncNotifyingListeningExecutorServiceTest.testListenerCallback;
+import static org.opendaylight.yangtools.util.concurrent.CommonTestUtils.Invoker;
+import static org.opendaylight.yangtools.util.concurrent.CommonTestUtils.SUBMIT_CALLABLE;
+import static org.opendaylight.yangtools.util.concurrent.CommonTestUtils.SUBMIT_RUNNABLE;
+import static org.opendaylight.yangtools.util.concurrent.CommonTestUtils.SUBMIT_RUNNABLE_WITH_RESULT;
 
 /**
  * Unit tests for DeadlockDetectingListeningExecutorService.
@@ -33,44 +42,6 @@ import com.google.common.util.concurrent.ListeningExecutorService;
  */
 public class DeadlockDetectingListeningExecutorServiceTest {
 
-    interface Invoker {
-        ListenableFuture<?> invokeExecutor( ListeningExecutorService executor );
-    };
-
-    static final Invoker SUBMIT_CALLABLE = new Invoker() {
-        @Override
-        public ListenableFuture<?> invokeExecutor( ListeningExecutorService executor ) {
-            return executor.submit( new Callable<String>() {
-                @Override
-                public String call() throws Exception{
-                    return "foo";
-                }
-            } );
-        }
-    };
-
-    static final Invoker SUBMIT_RUNNABLE =  new Invoker() {
-        @Override
-        public ListenableFuture<?> invokeExecutor( ListeningExecutorService executor ) {
-            return executor.submit( new Runnable() {
-                @Override
-                public void run(){
-                }
-            } );
-        }
-    };
-
-    static final Invoker SUBMIT_RUNNABLE_WITH_RESULT = new Invoker() {
-        @Override
-        public ListenableFuture<?> invokeExecutor( ListeningExecutorService executor ) {
-            return executor.submit( new Runnable() {
-                @Override
-                public void run(){
-                }
-            }, "foo" );
-        }
-    };
-
     interface InitialInvoker {
         void invokeExecutor( ListeningExecutorService executor, Runnable task );
     };
@@ -104,13 +75,25 @@ public class DeadlockDetectingListeningExecutorServiceTest {
 
     @Before
     public void setup() {
-        executor = new DeadlockDetectingListeningExecutorService( Executors.newSingleThreadExecutor(),
-                                                                  DEADLOCK_EXECUTOR_FUNCTION );
+    }
+
+    @After
+    public void tearDown() {
+        if( executor != null ) {
+            executor.shutdownNow();
+        }
+    }
+
+    DeadlockDetectingListeningExecutorService newExecutor() {
+        return new DeadlockDetectingListeningExecutorService( Executors.newSingleThreadExecutor(),
+                DEADLOCK_EXECUTOR_FUNCTION );
     }
 
     @Test
     public void testBlockingSubmitOffExecutor() throws Exception {
 
+        executor = newExecutor();
+
         // Test submit with Callable.
 
         ListenableFuture<String> future = executor.submit( new Callable<String>() {
@@ -144,6 +127,8 @@ public class DeadlockDetectingListeningExecutorServiceTest {
     @Test
     public void testNonBlockingSubmitOnExecutorThread() throws Throwable {
 
+        executor = newExecutor();
+
         testNonBlockingSubmitOnExecutorThread( SUBMIT, SUBMIT_CALLABLE );
         testNonBlockingSubmitOnExecutorThread( SUBMIT, SUBMIT_RUNNABLE );
         testNonBlockingSubmitOnExecutorThread( SUBMIT, SUBMIT_RUNNABLE_WITH_RESULT );
@@ -162,7 +147,7 @@ public class DeadlockDetectingListeningExecutorServiceTest {
             @Override
             public void run() {
 
-                Futures.addCallback( invoker.invokeExecutor( executor ), new FutureCallback() {
+                Futures.addCallback( invoker.invokeExecutor( executor, null ), new FutureCallback() {
                     @Override
                     public void onSuccess( Object result ) {
                         futureCompletedLatch.countDown();
@@ -191,6 +176,8 @@ public class DeadlockDetectingListeningExecutorServiceTest {
     @Test
     public void testBlockingSubmitOnExecutorThread() throws Exception {
 
+        executor = newExecutor();
+
         testBlockingSubmitOnExecutorThread( SUBMIT, SUBMIT_CALLABLE );
         testBlockingSubmitOnExecutorThread( SUBMIT, SUBMIT_RUNNABLE );
         testBlockingSubmitOnExecutorThread( SUBMIT, SUBMIT_RUNNABLE_WITH_RESULT );
@@ -209,7 +196,7 @@ public class DeadlockDetectingListeningExecutorServiceTest {
             public void run() {
 
                 try {
-                    invoker.invokeExecutor( executor ).get();
+                    invoker.invokeExecutor( executor, null ).get();
                 } catch( ExecutionException e ) {
                     caughtEx.set( e.getCause() );
                 } catch( Throwable e ) {
@@ -229,4 +216,25 @@ public class DeadlockDetectingListeningExecutorServiceTest {
         assertNotNull( "Expected exception thrown", caughtEx.get() );
         assertEquals( "Caught exception type", TestDeadlockException.class, caughtEx.get().getClass() );
     }
+
+    @Test
+    public void testListenableFutureCallbackWithExecutor() throws InterruptedException {
+
+        String listenerThreadPrefix = "ListenerThread";
+        ExecutorService listenerExecutor = Executors.newFixedThreadPool( 1,
+                new ThreadFactoryBuilder().setNameFormat( listenerThreadPrefix + "-%d" ).build() );
+
+        executor = new DeadlockDetectingListeningExecutorService(
+                Executors.newSingleThreadExecutor(
+                        new ThreadFactoryBuilder().setNameFormat( "SingleThread" ).build() ),
+                DEADLOCK_EXECUTOR_FUNCTION, listenerExecutor );
+
+        try {
+            testListenerCallback( executor, SUBMIT_CALLABLE, listenerThreadPrefix );
+            testListenerCallback( executor, SUBMIT_RUNNABLE, listenerThreadPrefix );
+            testListenerCallback( executor, SUBMIT_RUNNABLE_WITH_RESULT, listenerThreadPrefix );
+        } finally {
+            listenerExecutor.shutdownNow();
+        }
+    }
 }
diff --git a/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/QueuedNotificationManagerTest.java b/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/QueuedNotificationManagerTest.java
new file mode 100644 (file)
index 0000000..d7e0e50
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+ * 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.concurrent;
+
+import static org.junit.Assert.fail;
+import static org.junit.Assert.assertEquals;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.After;
+import org.junit.Test;
+
+import com.google.common.base.Stopwatch;
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.Uninterruptibles;
+
+/**
+ * Unit tests for QueuedNotificationManager.
+ *
+ * @author Thomas Pantelis
+ */
+public class QueuedNotificationManagerTest {
+
+    static class TestListener<N> {
+
+        private final List<N> actual;
+        private volatile int expCount;
+        private volatile CountDownLatch latch;
+        volatile long sleepTime = 0;
+        volatile RuntimeException runtimeEx;
+        volatile Error jvmError;
+        boolean cacheNotifications = true;
+        String name;
+
+        TestListener( int expCount, int id ) {
+            name = "TestListener " + id;
+            actual = Collections.synchronizedList( Lists.<N>newArrayListWithCapacity( expCount ) );
+            reset( expCount );
+        }
+
+        void reset( int expCount ) {
+            this.expCount = expCount;
+            latch = new CountDownLatch( expCount );
+            actual.clear();
+        }
+
+        void onNotification( N data ) {
+
+            try {
+                if( sleepTime > 0 ) {
+                    Uninterruptibles.sleepUninterruptibly( sleepTime, TimeUnit.MILLISECONDS );
+                }
+
+                if( cacheNotifications ) {
+                    actual.add( data );
+                }
+
+                RuntimeException localRuntimeEx = runtimeEx;
+                if( localRuntimeEx != null ) {
+                    runtimeEx = null;
+                    throw localRuntimeEx;
+                }
+
+                Error localJvmError = jvmError;
+                if( localJvmError != null ) {
+                    jvmError = null;
+                    throw localJvmError;
+                }
+
+            } finally {
+                latch.countDown();
+            }
+        }
+
+        void verifyNotifications() {
+            boolean done = Uninterruptibles.awaitUninterruptibly( latch, 10, TimeUnit.SECONDS );
+            if( !done ) {
+                long actualCount = latch.getCount();
+                fail( name + ": Received " + (expCount - actualCount) +
+                      " notifications. Expected " + expCount );
+            }
+        }
+
+        void verifyNotifications( List<N> expected ) {
+            verifyNotifications();
+            assertEquals( name + ": Notifications", Lists.newArrayList( expected ), actual );
+        }
+
+        // Implement bad hashCode/equals methods to verify it doesn't screw up the
+        // QueuedNotificationManager as it should use reference identity.
+        @Override
+        public int hashCode(){
+            return 1;
+        }
+
+        @Override
+        public boolean equals( Object obj ){
+            TestListener<?> other = (TestListener<?>) obj;
+            return other != null;
+        }
+    }
+
+    static class TestListener2<N> extends TestListener<N> {
+        TestListener2( int expCount, int id  ) {
+            super(expCount, id);
+        }
+    }
+
+    static class TestListener3<N> extends TestListener<N> {
+        TestListener3( int expCount, int id ) {
+            super(expCount, id);
+        }
+    }
+
+    static class TestNotifier<N> implements QueuedNotificationManager.Invoker<TestListener<N>,N> {
+
+        @Override
+        public void invokeListener( TestListener<N> listener, N notification ) {
+            listener.onNotification( notification );
+        }
+    }
+
+    private ExecutorService queueExecutor;
+
+    @After
+    public void tearDown() {
+        if( queueExecutor != null ) {
+            queueExecutor.shutdownNow();
+        }
+    }
+
+    @Test(timeout=10000)
+    public void testNotificationsWithSingleListener() {
+
+        queueExecutor = Executors.newFixedThreadPool( 2 );
+        NotificationManager<TestListener<Integer>, Integer> manager =
+                new QueuedNotificationManager<>( queueExecutor, new TestNotifier<Integer>(),
+                10, "TestMgr" );
+
+        int initialCount = 6;
+        int nNotifications = 100;
+
+        TestListener<Integer> listener = new TestListener<>( nNotifications, 1 );
+        listener.sleepTime = 20;
+
+        manager.submitNotifications( listener, Arrays.asList( 1, 2 ) );
+        manager.submitNotification( listener, 3 );
+        manager.submitNotifications( listener, Arrays.asList( 4, 5 ) );
+        manager.submitNotification( listener, 6 );
+
+        manager.submitNotifications( null, Collections.<Integer>emptyList() );
+        manager.submitNotifications( listener, null );
+        manager.submitNotification( listener, null );
+
+        Uninterruptibles.sleepUninterruptibly( 100, TimeUnit.MILLISECONDS );
+
+        listener.sleepTime = 0;
+
+        List<Integer> expNotifications = Lists.newArrayListWithCapacity( nNotifications );
+        expNotifications.addAll( Arrays.asList( 1, 2, 3, 4, 5, 6 ) );
+        for( int i = 1; i <= nNotifications - initialCount; i++ ) {
+            Integer v = Integer.valueOf( initialCount + i );
+            expNotifications.add( v );
+            manager.submitNotification( listener, v );
+        }
+
+        listener.verifyNotifications( expNotifications );
+    }
+
+    @Test
+    public void testNotificationsWithMultipleListeners() {
+
+        int nListeners = 10;
+        queueExecutor = Executors.newFixedThreadPool( nListeners );
+        final ExecutorService stagingExecutor = Executors.newFixedThreadPool( nListeners );
+        final NotificationManager<TestListener<Integer>, Integer> manager =
+                new QueuedNotificationManager<>( queueExecutor, new TestNotifier<Integer>(),
+                5000, "TestMgr" );
+
+        final int nNotifications = 100000;
+
+        System.out.println( "Testing " + nListeners + " listeners with " + nNotifications +
+                            " notifications each..." );
+
+        final Integer[] notifications = new Integer[nNotifications];
+        for( int i = 1; i <= nNotifications; i++ ) {
+            notifications[i-1] = Integer.valueOf( i );
+        }
+
+        Stopwatch stopWatch = new Stopwatch();
+        stopWatch.start();
+
+        List<TestListener<Integer>> listeners = Lists.newArrayList();
+        for( int i = 1; i <= nListeners; i++ ) {
+            final TestListener<Integer> listener =
+                    i == 2 ? new TestListener2<Integer>( nNotifications, i ) :
+                    i == 3 ? new TestListener3<Integer>( nNotifications, i ) :
+                                      new TestListener<Integer>( nNotifications, i );
+            listeners.add( listener );
+
+            new Thread( new Runnable() {
+                @Override
+                public void run() {
+                    for( int j = 1; j <= nNotifications; j++ ) {
+                        final Integer n = notifications[j-1];
+                        stagingExecutor.execute( new Runnable() {
+                            @Override
+                            public void run() {
+                                manager.submitNotification( listener, n );
+                            }
+                        } );
+                    }
+                }
+            } ).start();
+        }
+
+        try {
+            for( TestListener<Integer> listener: listeners ) {
+                listener.verifyNotifications();
+                System.out.println( listener.name + " succeeded" );
+            }
+        } finally {
+            stagingExecutor.shutdownNow();
+        }
+
+        stopWatch.stop();
+
+        System.out.println( "Elapsed time: " + stopWatch );
+        System.out.println( queueExecutor );
+    }
+
+    @Test(timeout=10000)
+    public void testNotificationsWithListenerRuntimeEx() {
+
+        queueExecutor = Executors.newFixedThreadPool( 1 );
+        NotificationManager<TestListener<Integer>, Integer> manager =
+                new QueuedNotificationManager<>( queueExecutor, new TestNotifier<Integer>(),
+                10, "TestMgr" );
+
+
+        TestListener<Integer> listener = new TestListener<>( 2, 1 );
+        listener.runtimeEx = new RuntimeException( "mock" );
+
+        manager.submitNotification( listener, 1 );
+        manager.submitNotification( listener, 2 );
+
+        listener.verifyNotifications();
+    }
+
+    @Test(timeout=10000)
+    public void testNotificationsWithListenerJVMError() {
+
+        final CountDownLatch errorCaughtLatch = new CountDownLatch( 1 );
+        queueExecutor = new ThreadPoolExecutor( 1, 1, 0, TimeUnit.SECONDS,
+                                                new LinkedBlockingQueue<Runnable>() ) {
+             @Override
+             public void execute( final Runnable command ) {
+                 super.execute( new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            command.run();
+                        } catch( Error e ) {
+                            errorCaughtLatch.countDown();
+                        }
+                    }
+                });
+             }
+        };
+
+        NotificationManager<TestListener<Integer>, Integer> manager =
+                new QueuedNotificationManager<>( queueExecutor, new TestNotifier<Integer>(),
+                10, "TestMgr" );
+
+        TestListener<Integer> listener = new TestListener<>( 2, 1 );
+        listener.jvmError = new Error( "mock" );
+
+        manager.submitNotification( listener, 1 );
+
+        assertEquals( "JVM Error caught", true, Uninterruptibles.awaitUninterruptibly(
+                                                       errorCaughtLatch, 5, TimeUnit.SECONDS ) );
+
+        manager.submitNotification( listener, 2 );
+
+        listener.verifyNotifications();
+    }
+}
diff --git a/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/ReflectiveExceptionMapperTest.java b/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/ReflectiveExceptionMapperTest.java
new file mode 100644 (file)
index 0000000..c18ac1f
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2014 Robert Varga.  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;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.concurrent.ExecutionException;
+
+import org.junit.Test;
+
+public final class ReflectiveExceptionMapperTest {
+    static final class NoArgumentCtorException extends Exception {
+        private static final long serialVersionUID = 1L;
+
+        public NoArgumentCtorException() {
+            super();
+        }
+    }
+
+    static final class PrivateCtorException extends Exception {
+        private static final long serialVersionUID = 1L;
+
+        private PrivateCtorException(final String message, final Throwable cause) {
+            super(message, cause);
+        }
+    }
+
+    static final class FailingCtorException extends Exception {
+        private static final long serialVersionUID = 1L;
+
+        public FailingCtorException(final String message, final Throwable cause) {
+            throw new IllegalArgumentException("just for test");
+        }
+    }
+
+    static final class GoodException extends Exception {
+        private static final long serialVersionUID = 1L;
+
+        public GoodException(final String message, final Throwable cause) {
+            super(message, cause);
+        }
+    }
+
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testNoArgumentsContructor() {
+        ReflectiveExceptionMapper.create("no arguments", NoArgumentCtorException.class);
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testPrivateContructor() {
+        ReflectiveExceptionMapper.create("private constructor", PrivateCtorException.class);
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testFailingContructor() {
+        ReflectiveExceptionMapper.create("failing constructor", FailingCtorException.class);
+    }
+
+    @Test
+    public void testInstantiation() {
+        ReflectiveExceptionMapper<GoodException> mapper = ReflectiveExceptionMapper.create("instantiation", GoodException.class);
+
+        final Throwable cause = new Throwable("some test message");
+
+        GoodException ret = mapper.apply(new ExecutionException("test", cause));
+
+        assertEquals("instantiation execution failed", ret.getMessage());
+        assertEquals(cause, ret.getCause());
+    }
+}
diff --git a/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/ThreadPoolExecutorTest.java b/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/ThreadPoolExecutorTest.java
new file mode 100644 (file)
index 0000000..8270e45
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * 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.concurrent;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.junit.After;
+import org.junit.Test;
+
+import com.google.common.base.Stopwatch;
+
+/**
+ * Tests various ThreadPoolExecutor implementations.
+ *
+ * @author Thomas Pantelis
+ */
+public class ThreadPoolExecutorTest {
+
+    private ExecutorService executor;
+
+    @After
+    public void tearDown() {
+        if( executor != null ) {
+            executor.shutdownNow();
+        }
+    }
+
+    @Test
+    public void testFastThreadPoolExecution() throws Exception {
+
+        testThreadPoolExecution(
+                SpecialExecutors.newBoundedFastThreadPool( 50, 100000, "TestPool" ),
+                100000, "TestPool", 0 );
+    }
+
+    @Test(expected=RejectedExecutionException.class)
+    public void testFastThreadPoolRejectingTask() throws Exception {
+
+        executor = SpecialExecutors.newBoundedFastThreadPool( 1, 1, "TestPool" );
+
+        for( int i = 0; i < 5; i++ ) {
+            executor.execute( new Task( null, null, null, null,
+                    TimeUnit.MICROSECONDS.convert( 5, TimeUnit.SECONDS ) ) );
+        }
+    }
+
+    @Test
+    public void testBlockingFastThreadPoolExecution() throws Exception {
+
+        // With a queue capacity of 1, it should block at some point.
+        testThreadPoolExecution(
+                SpecialExecutors.newBlockingBoundedFastThreadPool( 2, 1, "TestPool" ),
+                1000, null, 10 );
+    }
+
+    @Test
+    public void testCachedThreadPoolExecution() throws Exception {
+
+        testThreadPoolExecution(
+                SpecialExecutors.newBoundedCachedThreadPool( 10, 100000, "TestPool" ),
+                100000, "TestPool", 0 );
+    }
+
+    @Test(expected=RejectedExecutionException.class)
+    public void testCachedThreadRejectingTask() throws Exception {
+
+        ExecutorService executor = SpecialExecutors.newBoundedCachedThreadPool( 1, 1, "TestPool" );
+
+        for( int i = 0; i < 5; i++ ) {
+            executor.execute( new Task( null, null, null, null,
+                    TimeUnit.MICROSECONDS.convert( 5, TimeUnit.SECONDS ) ) );
+        }
+    }
+
+    @Test
+    public void testBlockingCachedThreadPoolExecution() throws Exception {
+
+        testThreadPoolExecution(
+                SpecialExecutors.newBlockingBoundedCachedThreadPool( 2, 1, "TestPool" ),
+                1000, null, 10 );
+    }
+
+    void testThreadPoolExecution( final ExecutorService executor,
+            final int numTasksToRun, final String expThreadPrefix, final long taskDelay ) throws Exception {
+
+        this.executor = executor;
+
+        System.out.println( "\nTesting " + executor.getClass().getSimpleName() + " with " +
+                numTasksToRun + " tasks." );
+
+        final CountDownLatch tasksRunLatch = new CountDownLatch( numTasksToRun );
+        final ConcurrentMap<Thread, AtomicLong> taskCountPerThread = new ConcurrentHashMap<>();
+        final AtomicReference<AssertionError> threadError = new AtomicReference<>();
+
+        Stopwatch stopWatch = new Stopwatch();
+        stopWatch.start();
+
+        new Thread() {
+            @Override
+            public void run() {
+                for( int i = 0; i < numTasksToRun; i++ ) {
+//                    if(i%100 == 0) {
+//                        Uninterruptibles.sleepUninterruptibly( 20, TimeUnit.MICROSECONDS );
+//                    }
+
+                    executor.execute( new Task( tasksRunLatch, taskCountPerThread,
+                                                threadError, expThreadPrefix, taskDelay ) );
+                }
+            }
+        }.start();
+
+        boolean done = tasksRunLatch.await( 15, TimeUnit.SECONDS );
+
+        stopWatch.stop();
+
+        if( !done ) {
+            fail( (numTasksToRun - tasksRunLatch.getCount()) + " tasks out of " +
+                   numTasksToRun + " executed" );
+        }
+
+        if( threadError.get() != null ) {
+            throw threadError.get();
+        }
+
+        System.out.println( taskCountPerThread.size() + " threads used:" );
+        for( Map.Entry<Thread, AtomicLong> e : taskCountPerThread.entrySet() ) {
+            System.out.println( "  " + e.getKey().getName() + " - " + e.getValue() + " tasks" );
+        }
+
+        System.out.println( "\n" + executor );
+        System.out.println( "\nElapsed time: " + stopWatch );
+        System.out.println();
+    }
+
+    private static class Task implements Runnable {
+        final CountDownLatch tasksRunLatch;
+        final ConcurrentMap<Thread, AtomicLong> taskCountPerThread;
+        final AtomicReference<AssertionError> threadError;
+        final String expThreadPrefix;
+        final long delay;
+
+        Task( CountDownLatch tasksRunLatch, ConcurrentMap<Thread, AtomicLong> taskCountPerThread,
+                AtomicReference<AssertionError> threadError, String expThreadPrefix, long delay ) {
+            this.tasksRunLatch = tasksRunLatch;
+            this.taskCountPerThread = taskCountPerThread;
+            this.threadError = threadError;
+            this.expThreadPrefix = expThreadPrefix;
+            this.delay = delay;
+        }
+
+        @Override
+        public void run() {
+            try {
+                if( delay > 0 ) {
+                    try {
+                        TimeUnit.MICROSECONDS.sleep( delay );
+                    } catch( InterruptedException e ) {}
+                }
+
+                if( expThreadPrefix != null ) {
+                    assertEquals( "Thread name starts with " + expThreadPrefix, true,
+                            Thread.currentThread().getName().startsWith( expThreadPrefix ) );
+                }
+
+                if( taskCountPerThread != null ) {
+                    AtomicLong count = taskCountPerThread.get( Thread.currentThread() );
+                    if( count == null ) {
+                        count = new AtomicLong( 0 );
+                        AtomicLong prev = taskCountPerThread.putIfAbsent( Thread.currentThread(), count );
+                        if( prev != null ) {
+                            count = prev;
+                        }
+                    }
+
+                    count.incrementAndGet();
+                }
+
+            } catch( AssertionError e ) {
+                if( threadError != null ) {
+                    threadError.set( e );
+                }
+            } finally {
+                if( tasksRunLatch != null ) {
+                    tasksRunLatch.countDown();
+                }
+            }
+        }
+    }
+}
index a8eef46d22944e10093fa2e4e60f970e79fd4714..a7e7cc77ada2232130cc69f2ce53e7cf9858615c 100644 (file)
@@ -29,7 +29,7 @@
         <module>yang-model-util</module>
         <module>yang-parser-api</module>
         <module>yang-parser-impl</module>
-        <module>yang-data-json</module>
+        <module>yang-data-composite-node</module>
     </modules>
     <build>
         <plugins>
index ba8b6812e2c911156ef34db5a7fa9b05fa7093b5..2966fb982fcb7a7cac76965d2e1c01b1e6df53a2 100644 (file)
@@ -9,18 +9,16 @@ package org.opendaylight.yangtools.yang.binding;
 
 import static com.google.common.base.Preconditions.checkArgument;
 
+import com.google.common.base.CharMatcher;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableSet;
 import java.text.SimpleDateFormat;
 import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
 
-import com.google.common.base.CharMatcher;
-import com.google.common.base.Splitter;
-import com.google.common.collect.ImmutableSet;
-
 public final class BindingMapping {
 
     public static final String VERSION = "0.6";
@@ -37,6 +35,7 @@ public final class BindingMapping {
     public static final String NOTIFICATION_LISTENER_SUFFIX = "Listener";
     public static final String QNAME_STATIC_FIELD_NAME = "QNAME";
     public static final String PACKAGE_PREFIX = "org.opendaylight.yang.gen.v1";
+    public static final String AUGMENTATION_FIELD = "augmentation";
 
     private static final Splitter CAMEL_SPLITTER = Splitter.on(CharMatcher.anyOf(" _.-").precomputed())
             .omitEmptyStrings().trimResults();
diff --git a/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/BindingStreamEventWriter.java b/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/BindingStreamEventWriter.java
new file mode 100644 (file)
index 0000000..921a78c
--- /dev/null
@@ -0,0 +1,406 @@
+/*
+ * 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;
+
+
+/**
+ * Event Stream Writer for Binding Representation
+ *
+ *
+ * <h3>Emmiting Event Stream</h3>
+ *
+ * <ul>
+ * <li><code>container</code> - Container node representation, start event is
+ * emitted using {@link #startContainerNode(Class, int)} and node end event is
+ * emitted using {@link #endNode()}. Container node is implementing
+ * {@link DataObject} interface.
+ *
+ * <li><code>list</code> - YANG list statement has two representation in event
+ * stream - unkeyed list and map. Unkeyed list is YANG list which did not
+ * specify key.</li>
+ *
+ * <ul>
+ * <li><code>Map</code> - Map start event is emitted using
+ * {@link #startMapNode(Class, int)} and is ended using {@link #endNode()}. Each map
+ * entry start is emitted using {@link #startMapEntryNode(Identifier, int)} with Map of keys
+ * and finished using {@link #endNode()}.</li>
+ *
+ * <li><code>UnkeyedList</code> - Unkeyed list represent list without keys,
+ * unkeyed list start is emmited using {@link #startUnkeyedList(Class, int)} list
+ * end is emmited using {@link #endNode()}. Each list item is emmited using
+ * {@link #startUnkeyedListItem()} and ended using {@link #endNode()}.</li>
+ * </ul>
+ *
+ * <li><code>leaf</code> - Leaf node event is emitted using
+ * {@link #leafNode(String, Object)}. {@link #endNode()} MUST be not emmited for
+ * leaf node.</li>
+ *
+ * <li><code>leaf-list</code> - Leaf list start is emitted using
+ * {@link #startLeafSet(String, int)}. Leaf list end is emitted using
+ * {@link #endNode()}. Leaf list entries are emmited using
+ * {@link #leafSetEntryNode(Object).
+ *
+ * <li><code>anyxml - Anyxml node event is emitted using
+ * {@link #leafNode(String, Object)}. {@link #endNode()} MUST be not emmited
+ * for anyxml node.</code></li>
+ *
+ *
+ * <li><code>choice</code> Choice node event is emmited by
+ * {@link #startChoiceNode(Class, int)} event and must be immediately followed by
+ * {@link #startCase(Class, int)} event. Choice node is finished by emitting
+ * {@link #endNode()} event.</li>
+ *
+ * <li>
+ * <code>case</code> - Case node may be emitted only inside choice node by
+ * invoking {@link #startCase(Class, int)}. Case node is finished be emitting
+ * {@link #endNode()} event.</li>
+ *
+ * <li>
+ * <code>augment</code> - Represents augmentation, augmentation node is started
+ * by invoking {@link #startAugmentationNode(Class)} and
+ * finished by invoking {@link #endNode()}.</li>
+ *
+ * </ul>
+ *
+ * <h3>Implementation notes</h3> This interface is not intended to be
+ * implemented by users of generated Binding DTOs but to be used by utilities,
+ * which needs to emit NormalizedNode model from Binding DTOs.
+ * <p>
+ * This interface is intended as API definition of facade for real Event /
+ * Stream Writer, without explicitly requiring stream writer and related
+ * interfaces to be imported by all generated Binding DTOs.
+ * <p>
+ * Existence of this interface in base Java Binding package is required to
+ * support runtime generation of users of this interface in OSGI and OSGI-like
+ * environment, since this package is only package which is imported by all
+ * generated Binding DTOs and wired in OSGI.
+ *
+ *
+ */
+public interface BindingStreamEventWriter {
+
+    /**
+     * Methods in this interface allow users to hint the underlying
+     * implementation about the sizing of container-like constructurs
+     * (leafLists, containers, etc.). These hints may be taken into account by a
+     * particular implementation to improve performance, but clients are not
+     * required to provide hints. This constant should be used by clients who
+     * either do not have the sizing information, or do not wish to divulge it
+     * (for whatever reasons). Implementations are free to ignore these hints
+     * completely, but if they do use them, they are expected to be resilient in
+     * face of missing and mismatched hints, which is to say the user can
+     * specify startLeafSet(..., 1) and then call leafNode() 15 times.
+     * <p>
+     * The acceptable hint values are non-negative integers and this constant,
+     * all other values will result, based on implementation preference, in the
+     * hint being completely ignored or IllegalArgumentException being thrown.
+     */
+    public final int UNKNOWN_SIZE = -1;
+
+    /**
+     *
+     * Emits a leaf node event with supplied value.
+     *
+     * @param localName
+     *            name of node as defined in schema, namespace and revision are
+     *            derived from parent node.
+     * @param value
+     *            Value of leaf node.
+     * @throws IllegalArgumentException
+     *             If emitted leaf node has invalid value in current context or
+     *             was emitted multiple times.
+     * @throws IllegalStateException
+     *             If node was emitted inside <code>map</code>,
+     *             <code>choice</code> <code>unkeyed list</code> node.
+     */
+    void leafNode(String localName, Object value) throws IllegalArgumentException;
+
+    /**
+     *
+     * Emits a start of leaf set (leaf-list).
+     * <p>
+     * Emits start of leaf set, during writing leaf set event, only
+     * {@link #leafSetEntryNode(Object)} calls are valid. Leaf set event is
+     * finished by calling {@link #endNode()}.
+     *
+     * @param localName
+     *            name of node as defined in schema, namespace and revision are
+     *            derived from parent node.
+     * @param childSizeHint
+     *            Non-negative count of expected direct child nodes or
+     *            {@link #UNKNOWN_SIZE} if count is unknown. This is only hint
+     *            and should not fail writing of child events, if there are more
+     *            events than count.
+     * @throws IllegalArgumentException
+     *             If emitted leaf node is invalid in current context or was
+     *             emitted multiple times.
+     * @throws IllegalStateException
+     *             If node was emitted inside <code>map</code>,
+     *             <code>choice</code> <code>unkeyed list</code> node.
+     */
+    void startLeafSet(String localName, int childSizeHint) throws IllegalArgumentException;
+
+    /**
+     * Emits a leaf set entry node
+     *
+     * @param value
+     *            Value of leaf set entry node.
+     * @throws IllegalArgumentException
+     *             If emitted leaf node has invalid value.
+     * @throws IllegalStateException
+     *             If node was emitted outside <code>leaf set</code> node.
+     */
+    void leafSetEntryNode(Object value) throws IllegalArgumentException;
+
+    /**
+     *
+     * Emits start of new container.
+     *
+     * <p>
+     * End of container event is emitted by invoking {@link #endNode()}.
+     *
+     * <p>
+     * Valid sub-events are:
+     * <ul>
+     * <li>{@link #leafNode(String, Object)}</li>
+     * <li>{@link #startContainerNode(Class, int)}</li>
+     * <li>{@link #startChoiceNode(Class, int)}</li>
+     * <li>{@link #startLeafSet(String, int)}</li>
+     * <li>{@link #startMapNode(Class, int)}</li>
+     * <li>{@link #startUnkeyedList(Class, int)}</li>
+     * <li>{@link #startAugmentationNode(Class)}</li>
+     * </ul>
+     *
+     * @param container
+     *            name of node as defined in schema, namespace and revision are
+     *            derived from parent node.
+     * @param childSizeHint
+     *            Non-negative count of expected direct child nodes or
+     *            {@link #UNKNOWN_SIZE} if count is unknown. This is only hint
+     *            and should not fail writing of child events, if there are more
+     *            events than count.
+     * @throws IllegalArgumentException
+     *             If emitted node is invalid in current context or was emitted
+     *             multiple times.
+     * @throws IllegalStateException
+     *             If node was emitted inside <code>map</code>,
+     *             <code>choice</code> <code>unkeyed list</code> node.
+     */
+    void startContainerNode(Class<? extends DataObject> container, int childSizeHint) throws IllegalArgumentException;
+
+    /**
+     *
+     * Emits start of unkeyed list node event.
+     *
+     * <p>
+     * End of unkeyed list event is emitted by invoking {@link #endNode()}.
+     * Valid subevents is only {@link #startUnkeyedListItem()}. All other
+     * methods will throw {@link IllegalArgumentException}.
+     *
+     * @param localName
+     *            name of node as defined in schema, namespace and revision are
+     *            derived from parent node.
+     * @param childSizeHint
+     *            Non-negative count of expected direct child nodes or
+     *            {@link #UNKNOWN_SIZE} if count is unknown. This is only hint
+     *            and should not fail writing of child events, if there are more
+     *            events than count.
+     * @throws IllegalArgumentException
+     *             If emitted node is invalid in current context or was emitted
+     *             multiple times.
+     * @throws IllegalStateException
+     *             If node was emitted inside <code>map</code>,
+     *             <code>choice</code> <code>unkeyed list</code> node.
+     */
+    void startUnkeyedList(Class<? extends DataObject> localName, int childSizeHint) throws IllegalArgumentException;
+
+    /**
+     * Emits start of new unkeyed list item.
+     *
+     * <p>
+     * Unkeyed list item event is finished by invoking {@link #endNode()}. Valid
+     * sub-events are:
+     * <p>
+     * Valid sub-events are:
+     *
+     * <ul>
+     * <li>{@link #leafNode(String, Object)}</li>
+     * <li>{@link #startContainerNode(Class, int)}</li>
+     * <li>{@link #startChoiceNode(Class, int)}</li>
+     * <li>{@link #startLeafSet(String, int)}</li>
+     * <li>{@link #startMapNode(Class, int)}</li>
+     * <li>{@link #startUnkeyedList(Class, int)}</li>
+     * <li>{@link #startAugmentationNode(Class)}</li>
+     * </ul>
+     *
+     *
+     * @param childSizeHint
+     *            Non-negative count of expected direct child nodes or
+     *            {@link #UNKNOWN_SIZE} if count is unknown. This is only hint
+     *            and should not fail writing of child events, if there are more
+     *            events than count.
+     * @throws IllegalStateException
+     *             If node was emitted outside <code>unkeyed list</code> node.
+     */
+    void startUnkeyedListItem(int childSizeHint) throws IllegalStateException;
+
+    /**
+     *
+     * Emits start of map node event.
+     *
+     * <p>
+     * End of map node event is emitted by invoking {@link #endNode()}. Valid
+     * subevents is only {@link #startMapEntryNode(Identifier, int)}. All other methods will
+     * throw {@link IllegalArgumentException}.
+     *
+     * @param mapEntryType
+     *            Class of list item, which has defined key.
+     * @param childSizeHint
+     *            Non-negative count of expected direct child nodes or
+     *            {@link #UNKNOWN_SIZE} if count is unknown. This is only hint
+     *            and should not fail writing of child events, if there are more
+     *            events than count.
+     * @throws IllegalArgumentException
+     * @throws IllegalStateException
+     *             If node was emitted inside <code>map</code>,
+     *             <code>choice</code> <code>unkeyed list</code> node.
+     */
+    <T extends DataObject & Identifiable<?>> void startMapNode(Class<T> mapEntryType, int childSizeHint)
+            throws IllegalArgumentException;
+
+    /**
+     *
+     * Emits start of map entry.
+     *
+     * <p>
+     * End of map entry event is emitted by invoking {@link #endNode()}.
+     *
+     * <p>
+     * Valid sub-events are:
+     * <<p>
+     * Valid sub-events are:
+     * <ul>
+     * <li>{@link #leafNode(String, Object)}</li>
+     * <li>{@link #startContainerNode(Class, int)}</li>
+     * <li>{@link #startChoiceNode(Class, int)}</li>
+     * <li>{@link #startLeafSet(String, int)}</li>
+     * <li>{@link #startMapNode(Class, int)}</li>
+     * <li>{@link #startUnkeyedList(Class, int)}</li>
+     * <li>{@link #startAugmentationNode(Class)}</li>
+     * </ul>
+     *
+     * @param keyValues
+     *            Key of map entry node
+     * @param childSizeHint
+     *            Non-negative count of expected direct child nodes or
+     *            {@link #UNKNOWN_SIZE} if count is unknown. This is only hint
+     *            and should not fail writing of child events, if there are more
+     *            events than count.
+     * @throws IllegalArgumentException
+     *             If key contains incorrect value.
+     * @throws IllegalStateException
+     *             If node was emitted outside <code>map entry</code> node.
+     */
+    void startMapEntryNode(Identifier<?> keyValues, int childSizeHint) throws IllegalArgumentException;
+
+    /**
+     * Emits start of choice node.
+     *
+     * <p>
+     * Valid sub-event in {@link #startCase(QName, int)}, which selects case
+     * which should be written.
+     * <ul>
+     *
+     * @param localName
+     *            name of node as defined in schema, namespace and revision are
+     *            derived from parent node.
+     * @param childSizeHint
+     *            Non-negative count of expected direct child nodes or
+     *            {@link #UNKNOWN_SIZE} if count is unknown. This is only hint
+     *            and should not fail writing of child events, if there are more
+     *            events than count.
+     * @throws IllegalArgumentException
+     * @throws IllegalStateException
+     *             If node was emitted inside <code>map</code>, <code>choice
+     *             </code> <code>unkeyed list</code> node.
+     */
+    void startChoiceNode(Class<? extends DataContainer> choice, int childSizeHint) throws IllegalArgumentException;
+
+    /**
+     *
+     * Starts a case node.
+     *
+     * <p>
+     * Valid sub-events are:
+     * <ul>
+     * <li>{@link #leafNode(String, Object)}</li>
+     * <li>{@link #startContainerNode(Class, int)}</li>
+     * <li>{@link #startChoiceNode(Class, int)}</li>
+     * <li>{@link #startLeafSet(String, int)}</li>
+     * <li>{@link #startMapNode(Class, int)}</li>
+     * <li>{@link #startUnkeyedList(Class, int)}</li>
+     * <li>{@link #startAugmentationNode(Class)}</li>
+     * </ul>
+     *
+     * @param name
+     * @throws IllegalArgumentException
+     */
+    void startCase(Class<? extends DataObject> caze, int childSizeHint) throws IllegalArgumentException;
+
+    /**
+     * Emits start of augmentation node.
+     *
+     * <p>
+     * End of augmentation event is emitted by invoking {@link #endNode()}.
+     *
+     * <p>
+     * Valid sub-events are:
+     *
+     * <p>
+     * Valid sub-events are:
+     * <ul>
+     * <li>{@link #leafNode(String, Object)}</li>
+     * <li>{@link #startContainerNode(Class, int)}</li>
+     * <li>{@link #startChoiceNode(Class, int)}</li>
+     * <li>{@link #startLeafSet(String, int)}</li>
+     * <li>{@link #startMapNode(Class, int)}</li>
+     * <li>{@link #startUnkeyedList(Class, int)}</li>
+     * </ul>
+     *
+     * <p>
+     * Note this is only method, which does not require childSizeHint, since
+     * maximum value is always size of <code>possibleChildren</code>.
+     *
+     * @param module
+     *            QName module of YANG module in which augmentation was defined
+     * @param possibleChildren
+     *            Local names of all valid children defined by augmentation.
+     * @throws IllegalArgumentException
+     *             If augmentation is invalid in current context.
+     */
+    void startAugmentationNode(Class<? extends Augmentation<?>> augmentationType) throws IllegalArgumentException;
+
+    /**
+     * Emits anyxml node event.
+     *
+     * @param name
+     * @param value
+     * @throws IllegalArgumentException
+     * @throws IllegalStateException
+     *             If node was emitted inside <code>map</code>,
+     *             <code>choice</code> <code>unkeyed list</code> node.
+     */
+    void anyxmlNode(String name, Object value) throws IllegalArgumentException;
+
+    /**
+     * Emits end event for node.
+     *
+     * @throws IllegalStateException If there is no open node.
+     */
+    void endNode() throws IllegalStateException;
+}
diff --git a/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/DataObjectSerializer.java b/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/DataObjectSerializer.java
new file mode 100644 (file)
index 0000000..90d9cc2
--- /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.binding;
+
+/**
+ *
+ * Serializer which writes DataObject to supplied stream event writer.
+ *
+ *
+ */
+public interface DataObjectSerializer {
+
+    /**
+     *
+     * Writes stream events representing object to supplied stream
+
+     *
+     * @param obj
+     *            Source of stream events
+     * @param stream
+     *            Stream to which events should be written.
+     */
+    void serialize(DataObject obj, BindingStreamEventWriter stream);
+
+}
diff --git a/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/DataObjectSerializerImplementation.java b/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/DataObjectSerializerImplementation.java
new file mode 100644 (file)
index 0000000..7aedc47
--- /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.binding;
+
+/**
+ * SPI-level contract for implementations of {@link DataObjectSerializer}.
+ * The contract is kept between implementation of {@link DataObjectSerializerRegistry},
+ * which maintains the lookup context required for recursive serialization.
+ *
+ * FIXME: this interface needs to be moved into .spi, but due to classpath funkyness
+ *        of OSGi, that change has to be carefully orchestrated to ensure proper imports
+ *        exist in all generated pacakges. One avenue how to achieve that is to move
+ *        {@link YangModuleInfo} and modify code generator to add a static field
+ *        to all generated classes which will point to the per-model YangModuleInfo
+ *        (currently all users of it have to walk the package hierarchy, so that
+ *        is an improvement in and of itself).
+ *
+ */
+public interface DataObjectSerializerImplementation {
+
+    /**
+     *
+     * Writes stream events for supplied data object to provided stream.
+     *
+     * DataObjectSerializerRegistry may be used to lookup serializers
+     * for other generated classes  in order to support writing
+     * their events.
+     *
+     */
+    void serialize(DataObjectSerializerRegistry reg,DataObject obj, BindingStreamEventWriter stream);
+
+}
diff --git a/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/DataObjectSerializerRegistry.java b/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/DataObjectSerializerRegistry.java
new file mode 100644 (file)
index 0000000..3c6a74b
--- /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.yang.binding;
+
+/**
+ * SPI-level contract for registry of {@link DataObjectSerializer}.
+ * The contract is kept between implementation of {@link DataObjectSerializerImplementation},
+ * Registry provides lookup for serializers to support recursive
+ * serialization of nested {@link DataObject}s.
+ *
+ * FIXME: this interface needs to be moved into .spi, but due to classpath funkyness
+ *        of OSGi, that change has to be carefully orchestrated to ensure proper imports
+ *        exist in all generated pacakges. One avenue how to achieve that is to move
+ *        {@link YangModuleInfo} and modify code generator to add a static field
+ *        to all generated classes which will point to the per-model YangModuleInfo
+ *        (currently all users of it have to walk the package hierarchy, so that
+ *        is an improvement in and of itself).
+ *
+ */
+public interface DataObjectSerializerRegistry {
+
+    DataObjectSerializer getSerializer(Class<? extends DataObject> binding);
+
+}
diff --git a/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/AugmentationFieldGetter.java b/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/AugmentationFieldGetter.java
new file mode 100644 (file)
index 0000000..8178d8f
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * 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.base.Preconditions;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import java.lang.reflect.Field;
+import java.util.Collections;
+import java.util.Map;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.BindingMapping;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+abstract class AugmentationFieldGetter {
+
+    private static final Logger LOG = LoggerFactory.getLogger(AugmentationFieldGetter.class);
+
+    private static final AugmentationFieldGetter DUMMY = new AugmentationFieldGetter() {
+        @Override
+        protected Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentations(final Object input) {
+            return Collections.emptyMap();
+        }
+    };
+
+   /**
+    *
+    * Retrieves augmentations from supplied object
+    *
+    * @param input Input Data object, from which augmentations should be extracted
+    * @return Map of Augmentation class to augmentation
+    */
+   protected abstract Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentations(final Object input);
+
+    private static final LoadingCache<Class<?>, AugmentationFieldGetter> AUGMENTATION_GETTERS =
+            CacheBuilder.newBuilder().weakKeys().softValues().build(new AugmentationGetterLoader());
+
+    public static AugmentationFieldGetter getGetter(final Class<? extends Object> clz) {
+        return AUGMENTATION_GETTERS.getUnchecked(clz);
+    }
+
+    private static final class AugmentationGetterLoader extends CacheLoader<Class<?>, AugmentationFieldGetter> {
+
+        @Override
+        public AugmentationFieldGetter load(final Class<?> key) throws Exception {
+            Field field;
+            try {
+                field = key.getDeclaredField(BindingMapping.AUGMENTATION_FIELD);
+            } catch (NoSuchFieldException | SecurityException e) {
+                LOG.debug("Failed to acquire augmentation field", e);
+                return DUMMY;
+            }
+            field.setAccessible(true);
+
+            return new ReflectionAugmentationFieldGetter(field);
+        }
+    }
+
+    private static final class ReflectionAugmentationFieldGetter extends AugmentationFieldGetter {
+        private final Field augmentationField;
+
+        ReflectionAugmentationFieldGetter(final Field augmentationField) {
+            this.augmentationField = Preconditions.checkNotNull(augmentationField);
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        protected Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentations(final Object input) {
+            try {
+                return (Map<Class<? extends Augmentation<?>>, Augmentation<?>>) augmentationField.get(input);
+            } catch (IllegalArgumentException | IllegalAccessException e) {
+                throw new IllegalStateException("Failed to access augmentation field", e);
+            }
+        }
+    }
+
+
+}
index d6c1d7b75138ab6da9ad5290cc83f4714459b7a3..81ef845de6596ee97417a86b84db5c02cb1cc1ee 100644 (file)
@@ -10,19 +10,25 @@ package org.opendaylight.yangtools.yang.binding.util;
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkState;
 
+import com.google.common.base.Optional;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSet.Builder;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Type;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 import java.util.ServiceLoader;
 import java.util.concurrent.Callable;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-
 import org.opendaylight.yangtools.yang.binding.Augmentable;
 import org.opendaylight.yangtools.yang.binding.Augmentation;
 import org.opendaylight.yangtools.yang.binding.BaseIdentity;
@@ -38,13 +44,6 @@ import org.opendaylight.yangtools.yang.common.QName;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Optional;
-import com.google.common.cache.CacheBuilder;
-import com.google.common.cache.CacheLoader;
-import com.google.common.cache.LoadingCache;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSet.Builder;
-
 public class BindingReflections {
 
     private static final long EXPIRATION_TIME = 60;
@@ -58,6 +57,7 @@ public class BindingReflections {
             .build(new ClassToQNameLoader());
 
 
+
     private BindingReflections() {
         throw new UnsupportedOperationException("Utility class.");
     }
@@ -539,5 +539,18 @@ public class BindingReflections {
                 moduleInfo.getName());
     }
 
+    /**
+    *
+    * Extracts augmentation from Binding DTO field using reflection
+    *
+    * @param input Instance of DataObject which is augmentable and
+    *      may contain augmentation
+    * @return Map of augmentations if read was successful, otherwise
+    *      empty map.
+    */
+   public static Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentations(final Augmentable<?> input) {
+       return AugmentationFieldGetter.getGetter(input.getClass()).getAugmentations(input);
+   }
+
 
 }
index fab50614f2dfccf85e73649b88d2c7071faec5ee..e9ddbfb2f151ffeb3ab8702cc6c03dbfeca38060 100644 (file)
@@ -9,6 +9,10 @@ package org.opendaylight.yangtools.yang.binding.util;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import com.google.common.base.Joiner;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Supplier;
+
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.ParameterizedType;
@@ -21,10 +25,10 @@ import java.util.concurrent.locks.Lock;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Joiner;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Supplier;
-
+/**
+ * @deprecated Use {@link org.opendaylight.yangtools.util.ClassLoaderUtils} instead.
+ */
+@Deprecated
 public final class ClassLoaderUtils {
     private static final Logger LOG = LoggerFactory.getLogger(ClassLoaderUtils.class);
 
@@ -58,17 +62,17 @@ public final class ClassLoaderUtils {
     }
 
     /**
-    *
-    * Runs {@link Callable} with provided {@link ClassLoader}.
-    *
-    * Invokes supplies function and makes sure that original {@link ClassLoader}
-    * is context {@link ClassLoader} after execution.
-    *
-    * @param cls {@link ClassLoader} to be used.
-    * @param function Function to be executed.
-    * @return Result of callable invocation.
-    *
-    */
+     *
+     * Runs {@link Callable} with provided {@link ClassLoader}.
+     *
+     * Invokes supplies function and makes sure that original {@link ClassLoader}
+     * is context {@link ClassLoader} after execution.
+     *
+     * @param cls {@link ClassLoader} to be used.
+     * @param function Function to be executed.
+     * @return Result of callable invocation.
+     *
+     */
     public static <V> V withClassLoader(final ClassLoader cls, final Callable<V> function) throws Exception {
         checkNotNull(cls, "Classloader should not be null");
         checkNotNull(function, "Function should not be null");
@@ -82,20 +86,20 @@ public final class ClassLoaderUtils {
         }
     }
 
-   /**
-    *
-    * Runs {@link Callable} with provided {@link ClassLoader} and Lock.
-    *
-    * Invokes supplies function after acquiring lock
-    * and makes sure that original {@link ClassLoader}
-    * is context {@link ClassLoader} and lock is unlocked
-    * after execution.
-    *
-    * @param cls {@link ClassLoader} to be used.
-    * @param function Function to be executed.
-    * @return Result of Callable invocation.
-    *
-    */
+    /**
+     *
+     * Runs {@link Callable} with provided {@link ClassLoader} and Lock.
+     *
+     * Invokes supplies function after acquiring lock
+     * and makes sure that original {@link ClassLoader}
+     * is context {@link ClassLoader} and lock is unlocked
+     * after execution.
+     *
+     * @param cls {@link ClassLoader} to be used.
+     * @param function Function to be executed.
+     * @return Result of Callable invocation.
+     *
+     */
     public static <V> V withClassLoaderAndLock(final ClassLoader cls, final Lock lock, final Supplier<V> function) {
         checkNotNull(lock, "Lock should not be null");
 
@@ -138,9 +142,9 @@ public final class ClassLoaderUtils {
             String potentialOuter;
             int length = components.length;
             if (length > 2 && (potentialOuter = components[length - 2]) != null && Character.isUpperCase(potentialOuter.charAt(0))) {
-                    String outerName = Joiner.on(".").join(Arrays.asList(components).subList(0, length - 1));
-                    String innerName = outerName + "$" + components[length-1];
-                    return cls.loadClass(innerName);
+                String outerName = Joiner.on(".").join(Arrays.asList(components).subList(0, length - 1));
+                String innerName = outerName + "$" + components[length-1];
+                return cls.loadClass(innerName);
             } else {
                 throw e;
             }
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/NormalizedNodeStreamWriter.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/NormalizedNodeStreamWriter.java
new file mode 100644 (file)
index 0000000..96b5c15
--- /dev/null
@@ -0,0 +1,368 @@
+/*
+ * 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.stream;
+
+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;
+
+
+/**
+ * Event Stream Writer based on Normalized Node tree representation
+ *
+ * <h3>Writing Event Stream</h3>
+ *
+ * <ul>
+ * <li><code>container</code> - Container node representation, start event is
+ * emitted using {@link #startContainerNode(NodeIdentifier, int)} and node end event is
+ * emitted using {@link #endNode()}. Container node is implementing
+ * {@link DataObject} interface.
+ *
+ * <li><code>list</code> - YANG list statement has two representation in event
+ * stream - unkeyed list and map. Unkeyed list is YANG list which did not
+ * specify key.</li>
+ *
+ * <ul>
+ * <li><code>Map</code> - Map start event is emitted using
+ * {@link #startMapNode(NodeIdentifier, int)} and is ended using {@link #endNode()}. Each map
+ * entry start is emitted using {@link #startMapEntryNode(NodeIdentifierWithPredicates, int)} with Map of keys
+ * and finished using {@link #endNode()}.</li>
+ *
+ * <li><code>UnkeyedList</code> - Unkeyed list represent list without keys,
+ * unkeyed list start is emmited using {@link #startUnkeyedList(NodeIdentifier, int)} list
+ * end is emmited using {@link #endNode()}. Each list item is emmited using
+ * {@link #startUnkeyedListItem(NodeIdentifier, int)} and ended using {@link #endNode()}.</li>
+ * </ul>
+ *
+ * <li><code>leaf</code> - Leaf node event is emitted using
+ * {@link #leafNode(NodeIdentifier, Object)}. {@link #endNode()} MUST NOT BE emmited for
+ * leaf node.</li>
+ *
+ * <li><code>leaf-list</code> - Leaf list start is emitted using
+ * {@link #startLeafSet(NodeIdentifier, int)}. Leaf list end is emitted using
+ * {@link #endNode()}. Leaf list entries are emmited using
+ * {@link #leafSetEntryNode(Object).
+ *
+ * <li><code>anyxml - Anyxml node event is emitted using
+ * {@link #leafNode(NodeIdentifier, Object)}. {@link #endNode()} MUST NOT BE emmited
+ * for anyxml node.</code></li>
+ *
+ *
+ * <li><code>choice</code> Choice node event is emmited by
+ * {@link #startChoiceNode(NodeIdentifier, int)} event and
+ * finished by invoking {@link #endNode()}
+ * <li>
+ * <code>augment</code> - Represents augmentation, augmentation node is started
+ * by invoking {@link #startAugmentationNode(AugmentationIdentifier)} and
+ * finished by invoking {@link #endNode()}.</li>
+ *
+ * </ul>
+ *
+ * <h3>Implementation notes</h3>
+ *
+ * <p>
+ * Implementations of this interface must not hold user suppled objects
+ * and resources needlessly.
+ *
+ */
+public interface NormalizedNodeStreamWriter {
+
+    /**
+     * Methods in this interface allow users to hint the underlying
+     * implementation about the sizing of container-like constructurs
+     * (leafLists, containers, etc.). These hints may be taken into account by a
+     * particular implementation to improve performance, but clients are not
+     * required to provide hints. This constant should be used by clients who
+     * either do not have the sizing information, or do not wish to divulge it
+     * (for whatever reasons). Implementations are free to ignore these hints
+     * completely, but if they do use them, they are expected to be resilient in
+     * face of missing and mismatched hints, which is to say the user can
+     * specify startLeafSet(..., 1) and then call leafNode() 15 times.
+     * <p>
+     * The acceptable hint values are non-negative integers and this constant,
+     * all other values will result, based on implementation preference, in the
+     * hint being completely ignored or IllegalArgumentException being thrown.
+     */
+    public final int UNKNOWN_SIZE = -1;
+
+    /**
+     *
+     * Emits a leaf node event with supplied value.
+     *
+     * @param name
+     *            name of node as defined in schema, namespace and revision are
+     *            derived from parent node.
+     * @param value
+     *            Value of leaf node. v
+     * @throws IllegalArgumentException
+     *             If emitted leaf node has invalid value in current context or
+     *             was emitted multiple times.
+     * @throws IllegalStateException
+     *             If node was emitted inside <code>map</code>,
+     *             <code>choice</code> <code>unkeyed list</code> node.
+     */
+    void leafNode(NodeIdentifier name, Object value) throws IllegalArgumentException;
+
+    /**
+     *
+     * Emits a start of leaf set (leaf-list).
+     * <p>
+     * Emits start of leaf set, during writing leaf set event, only
+     * {@link #leafSetEntryNode(Object)} calls are valid. Leaf set event is
+     * finished by calling {@link #endNode()}.
+     *
+     * @param name
+     *            name of node as defined in schema, namespace and revision are
+     *            derived from parent node.
+     * @param childSizeHint
+     *            Non-negative count of expected direct child nodes or
+     *            {@link #UNKNOWN_SIZE} if count is unknown. This is only hint
+     *            and should not fail writing of child events, if there are more
+     *            events than count.
+     * @throws IllegalArgumentException
+     *             If emitted leaf node is invalid in current context or was
+     *             emitted multiple times.
+     * @throws IllegalStateException
+     *             If node was emitted inside <code>map</code>,
+     *             <code>choice</code> <code>unkeyed list</code> node.
+     */
+    void startLeafSet(NodeIdentifier name, int childSizeHint) throws IllegalArgumentException;
+
+    /**
+     * Emits a leaf set entry node
+     *
+     * @param value
+     *            Value of leaf set entry node. Supplied object MUST BE constant over time.
+     * @throws IllegalArgumentException
+     *             If emitted leaf node has invalid value.
+     * @throws IllegalStateException
+     *             If node was emitted outside <code>leaf set</code> node.
+     */
+    void leafSetEntryNode(Object value) throws IllegalArgumentException;
+
+    /**
+     *
+     * Emits start of new container.
+     *
+     * <p>
+     * End of container event is emitted by invoking {@link #endNode()}.
+     *
+     * <p>
+     * Valid sub-events are:
+     * <ul>
+     * <li>{@link #leafNode}</li>
+     * <li>{@link #startContainerNode(NodeIdentifier, int)}</li>
+     * <li>{@link #startChoiceNode(NodeIdentifier, int)}</li>
+     * <li>{@link #startLeafSet(NodeIdentifier, int)}</li>
+     * <li>{@link #startMapNode(NodeIdentifier, int)}</li>
+     * <li>{@link #startUnkeyedList(NodeIdentifier, int)}</li>
+    * <li>{@link #startAugmentationNode(AugmentationIdentifier)}</li>
+    * </ul>
+     *
+     * @param name
+     *            name of node as defined in schema, namespace and revision are
+     *            derived from parent node.
+     * @param childSizeHint
+     *            Non-negative count of expected direct child nodes or
+     *            {@link #UNKNOWN_SIZE} if count is unknown. This is only hint
+     *            and should not fail writing of child events, if there are more
+     *            events than count.
+     * @throws IllegalArgumentException
+     *             If emitted node is invalid in current context or was emitted
+     *             multiple times.
+     * @throws IllegalStateException
+     *             If node was emitted inside <code>map</code>,
+     *             <code>choice</code> <code>unkeyed list</code> node.
+     */
+    void startContainerNode(NodeIdentifier name, int childSizeHint) throws IllegalArgumentException;
+
+    /**
+     *
+     * Emits start of unkeyed list node event.
+     *
+     * <p>
+     * End of unkeyed list event is emitted by invoking {@link #endNode()}.
+     * Valid subevents is only {@link #startUnkeyedListItem(NodeIdentifier, int)}. All other
+     * methods will throw {@link IllegalArgumentException}.
+     *
+     * @param name
+     *            name of node as defined in schema, namespace and revision are
+     *            derived from parent node.
+     * @param childSizeHint
+     *            Non-negative count of expected direct child nodes or
+     *            {@link #UNKNOWN_SIZE} if count is unknown. This is only hint
+     *            and should not fail writing of child events, if there are more
+     *            events than count.
+     * @throws IllegalArgumentException
+     *             If emitted node is invalid in current context or was emitted
+     *             multiple times.
+     * @throws IllegalStateException
+     *             If node was emitted inside <code>map</code>,
+     *             <code>choice</code> <code>unkeyed list</code> node.
+     */
+    void startUnkeyedList(NodeIdentifier name, int childSizeHint) throws IllegalArgumentException;
+
+    /**
+     * Emits start of new unkeyed list item.
+     *
+     * <p>
+     * Unkeyed list item event is finished by invoking {@link #endNode()}. Valid
+     * sub-events are:
+     * <ul>
+     * <li>{@link #leafNode}</li>
+     * <li>{@link #startContainerNode(NodeIdentifier, int)}</li>
+     * <li>{@link #startChoiceNode(NodeIdentifier, int)}</li>
+     * <li>{@link #startLeafSet(NodeIdentifier, int)}</li>
+     * <li>{@link #startMapNode(NodeIdentifier, int)}</li>
+     * <li>{@link #startUnkeyedList(NodeIdentifier, int)}</li>
+     * <li>{@link #startAugmentationNode(AugmentationIdentifier)}</li>
+     * </ul>
+     *
+     * @param name Identifier of node
+     * @param childSizeHint
+     *            Non-negative count of expected direct child nodes or
+     *            {@link #UNKNOWN_SIZE} if count is unknown. This is only hint
+     *            and should not fail writing of child events, if there are more
+     *            events than count.
+     * @throws IllegalStateException
+     *             If node was emitted outside <code>unkeyed list</code> node.
+     */
+    void startUnkeyedListItem(NodeIdentifier name, int childSizeHint) throws IllegalStateException;
+
+    /**
+     *
+     * Emits start of map node event.
+     *
+     * <p>
+     * End of map node event is emitted by invoking {@link #endNode()}. Valid
+     * subevents is only
+     * {@link #startMapEntryNode(NodeIdentifierWithPredicates, int)}. All other
+     * methods will throw {@link IllegalArgumentException}.
+     *
+     * @param name
+     *            name of node as defined in schema, namespace and revision are
+     *            derived from parent node.
+     * @param childSizeHint
+     * @throws IllegalArgumentException
+     * @throws IllegalStateException
+     *             If node was emitted inside <code>map</code>,
+     *             <code>choice</code> <code>unkeyed list</code> node.
+     */
+    void startMapNode(NodeIdentifier name, int childSizeHint) throws IllegalArgumentException;
+
+    /**
+     *
+     * Emits start of map entry.
+     *
+     * <p>
+     * End of map entry event is emitted by invoking {@link #endNode()}.
+     *
+     * <p>
+     * Valid sub-events are:
+     * <ul>
+     * <li>{@link #leafNode}</li>
+     * <li>{@link #startContainerNode(NodeIdentifier, int)}</li>
+     * <li>{@link #startChoiceNode(NodeIdentifier, int)}</li>
+     * <li>{@link #startLeafSet(NodeIdentifier, int)}</li>
+     * <li>{@link #startMapNode(NodeIdentifier, int)}</li>
+     * <li>{@link #startUnkeyedList(NodeIdentifier, int)}</li>
+     * <li>{@link #startAugmentationNode(AugmentationIdentifier)}</li>
+     * </ul>
+     *
+     *
+     * @param identifier
+     *            QName to value pairs of keys of map entry node. Values  MUST BE constant over time.
+     * @throws IllegalArgumentException
+     *             If key contains incorrect value.
+     * @throws IllegalStateException
+     *             If node was emitted outside <code>map entry</code> node.
+     */
+    void startMapEntryNode(NodeIdentifierWithPredicates identifier, int childSizeHint) throws IllegalArgumentException;
+
+    /**
+     *
+     * Emits start of map node event.
+     *
+     * <p>
+     * End of map node event is emitted by invoking {@link #endNode()}. Valid
+     * subevents is only
+     * {@link #startMapEntryNode(NodeIdentifierWithPredicates, int)}. All other
+     * methods will throw {@link IllegalArgumentException}.
+     *
+     * @param name
+     *            name of node as defined in schema, namespace and revision are
+     *            derived from parent node.
+     * @throws IllegalArgumentException
+     * @throws IllegalStateException
+     *             If node was emitted inside <code>map</code>,
+     *             <code>choice</code> <code>unkeyed list</code> node.
+     */
+    void startOrderedMapNode(NodeIdentifier name, int childSizeHint) throws IllegalArgumentException;
+
+    /**
+     *
+     *
+     *
+     * @param name
+     *            name of node as defined in schema, namespace and revision are
+     *            derived from parent node.
+     * @param childSizeHint
+     * @throws IllegalArgumentException
+     * @throws IllegalStateException
+     *             If node was emitted inside <code>map</code>,
+     *             <code>choice</code> <code>unkeyed list</code> node.
+     */
+    void startChoiceNode(NodeIdentifier name, int childSizeHint) throws IllegalArgumentException;
+
+    /**
+     * Emits start of augmentation node.
+     *
+     * <p>
+     * End of augmentation event is emitted by invoking {@link #endNode()}.
+     *
+     * <p>
+     * Valid sub-events are:
+     *
+     * <ul>
+     * <li>{@link #leafNode}</li>
+     * <li>{@link #startContainerNode(NodeIdentifier, int)}</li>
+     * <li>{@link #startChoiceNode(NodeIdentifier, int)}</li>
+     * <li>{@link #startLeafSet(NodeIdentifier, int)}</li>
+     * <li>{@link #startMapNode(NodeIdentifier, int)}</li>
+     * <li>{@link #startUnkeyedList(NodeIdentifier, int)}</li>
+     * </ul>
+     *
+     * @param identifier
+     *            Augmentation identifier
+     * @throws IllegalArgumentException
+     *             If augmentation is invalid in current context.
+     */
+    void startAugmentationNode(AugmentationIdentifier identifier) throws IllegalArgumentException;
+
+    /**
+     * Emits anyxml node event.
+     *
+     *
+     * @param name
+     * @param value
+     * @throws IllegalArgumentException
+     * @throws IllegalStateException
+     *             If node was emitted inside <code>map</code>,
+     *             <code>choice</code> <code>unkeyed list</code> node.
+     */
+    void anyxmlNode(NodeIdentifier name, Object value) throws IllegalArgumentException;
+
+    /**
+     * Emits end event for node.
+     *
+     * @throws IllegalStateException If there is no start* event to be closed.B
+     *
+     */
+    void endNode() throws IllegalStateException;
+
+}
similarity index 76%
rename from yang/yang-data-json/pom.xml
rename to yang/yang-data-composite-node/pom.xml
index 228b9d82d2892afcfd18ea547795548dcfc7d751..d4b1533cd2f055ac98759266f30e834061e6a401 100644 (file)
        </parent>
 
        <modelVersion>4.0.0</modelVersion>
-       <artifactId>yang-data-json</artifactId>
+       <artifactId>yang-data-composite-node</artifactId>
        <name>${project.artifactId}</name>
        <description>${project.artifactId}</description>
+    <packaging>bundle</packaging>
 
     <dependencies>
         <dependency>
         </dependency>
     </dependencies>
 
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-Name>Composite node Normalized node transformator</Bundle-Name>
+            <Import-Package>*</Import-Package>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
 </project>
diff --git a/yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/parser/AnyXmlNodeCnSnParser.java b/yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/parser/AnyXmlNodeCnSnParser.java
new file mode 100644 (file)
index 0000000..a13dd4b
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * 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
+ */
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.parser;
+
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.parser.AnyXmlNodeBaseParser;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
+
+public class AnyXmlNodeCnSnParser extends AnyXmlNodeBaseParser<Node<?>> {
+
+    public AnyXmlNodeCnSnParser() {
+        super();
+    }
+
+    @Override
+    protected Node<?> parseAnyXml(Node<?> element, AnyXmlSchemaNode schema) {
+        return element;
+    }
+}
@@ -5,13 +5,14 @@
  * 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.json.schema.cnsn.parser;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.parser;
+
+import org.opendaylight.yangtools.yang.data.composite.node.schema.json.CnSnToNormalizedNodesUtils;
 
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.parser.AugmentationNodeBaseParser;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.parser.NodeParserDispatcher;
-import org.opendaylight.yangtools.yang.data.json.schema.json.CnSnToNormalizedNodesUtils;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.LinkedListMultimap;
@@ -5,13 +5,14 @@
  * 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.json.schema.cnsn.parser;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.parser;
+
+import org.opendaylight.yangtools.yang.data.composite.node.schema.json.CnSnToNormalizedNodesUtils;
 
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.parser.ChoiceNodeBaseParser;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.parser.NodeParserDispatcher;
-import org.opendaylight.yangtools.yang.data.json.schema.json.CnSnToNormalizedNodesUtils;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.LinkedListMultimap;
@@ -5,9 +5,10 @@
  * 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.json.schema.cnsn.parser;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.parser;
 
 import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
@@ -18,6 +19,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.ToNormalizedNodeParser;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.ToNormalizedNodeParserFactory;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.parser.NodeParserDispatcher;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
@@ -35,11 +37,13 @@ public class CnSnToNormalizedNodeParserFactory implements ToNormalizedNodeParser
     private final MapEntryNodeCnSnParser mapEntryNodeCnSnParser;
     private final ChoiceNodeCnSnParser choiceNodeCnSnParser;
     private final AugmentationNodeCnSnParser augmentationNodeCnSnParser;
+    private final AnyXmlNodeCnSnParser anyXmlNodeCnSnParser;
 
     private CnSnToNormalizedNodeParserFactory() {
         leafNodeCnSnParser = new LeafNodeCnSnParser();
         leafSetEntryNodeCnSnParser = new LeafSetEntryNodeCnSnParser();
         leafSetNodeCnSnParser = new LeafSetNodeCnSnParser(leafSetEntryNodeCnSnParser);
+        anyXmlNodeCnSnParser = new AnyXmlNodeCnSnParser();
 
         final NodeParserDispatcher<Node<?>> dispatcher = new NodeParserDispatcher.BaseNodeParserDispatcher<Node<?>>(
                 this) {
@@ -51,6 +55,7 @@ public class CnSnToNormalizedNodeParserFactory implements ToNormalizedNodeParser
         mapNodeCnSnParser = new MapNodeCnSnParser(mapEntryNodeCnSnParser);
         choiceNodeCnSnParser = new ChoiceNodeCnSnParser(dispatcher);
         augmentationNodeCnSnParser = new AugmentationNodeCnSnParser(dispatcher);
+
     }
 
     public static CnSnToNormalizedNodeParserFactory getInstance() {
@@ -96,4 +101,9 @@ public class CnSnToNormalizedNodeParserFactory implements ToNormalizedNodeParser
     public ToNormalizedNodeParser<Node<?>, MapEntryNode, ListSchemaNode> getMapEntryNodeParser() {
         return mapEntryNodeCnSnParser;
     }
+
+    @Override
+    public ToNormalizedNodeParser<Node<?>, AnyXmlNode, AnyXmlSchemaNode> getAnyXmlNodeParser() {
+        return anyXmlNodeCnSnParser;
+    }
 }
@@ -5,7 +5,9 @@
  * 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.json.schema.cnsn.parser;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.parser;
+
+import org.opendaylight.yangtools.yang.data.composite.node.schema.json.CnSnToNormalizedNodesUtils;
 
 import java.util.Collections;
 import java.util.Map;
@@ -14,7 +16,6 @@ import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.parser.ContainerNodeBaseParser;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.parser.NodeParserDispatcher;
-import org.opendaylight.yangtools.yang.data.json.schema.json.CnSnToNormalizedNodesUtils;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.LinkedListMultimap;
@@ -5,7 +5,7 @@
  * 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.json.schema.cnsn.parser;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.parser;
 
 import java.util.Collections;
 import java.util.Map;
@@ -5,7 +5,7 @@
  * 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.json.schema.cnsn.parser;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.parser;
 
 import java.util.Collections;
 import java.util.Map;
@@ -5,7 +5,7 @@
  * 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.json.schema.cnsn.parser;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.parser;
 
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
@@ -5,7 +5,9 @@
  * 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.json.schema.cnsn.parser;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.parser;
+
+import org.opendaylight.yangtools.yang.data.composite.node.schema.json.CnSnToNormalizedNodesUtils;
 
 import java.util.Collections;
 import java.util.Map;
@@ -14,7 +16,6 @@ import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.parser.MapEntryNodeBaseParser;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.parser.NodeParserDispatcher;
-import org.opendaylight.yangtools.yang.data.json.schema.json.CnSnToNormalizedNodesUtils;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.LinkedListMultimap;
@@ -5,7 +5,7 @@
  * 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.json.schema.cnsn.parser;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.parser;
 
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
diff --git a/yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/serializer/AnyXmlNodeCnSnSerializer.java b/yang/yang-data-composite-node/src/main/java/org/opendaylight/yangtools/yang/data/composite/node/schema/cnsn/serializer/AnyXmlNodeCnSnSerializer.java
new file mode 100644 (file)
index 0000000..db941ed
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * 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
+ */
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.serializer;
+
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer.AnyXmlNodeBaseSerializer;
+
+public class AnyXmlNodeCnSnSerializer extends AnyXmlNodeBaseSerializer<Node<?>> {
+
+    @Override
+    protected Node<?> serializeAnyXml(AnyXmlNode node) {
+        return node.getValue();
+    }
+}
@@ -5,7 +5,7 @@
  * 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.json.schema.cnsn.serializer;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.serializer;
 
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer.AugmentationNodeBaseSerializer;
@@ -5,7 +5,7 @@
  * 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.json.schema.cnsn.serializer;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.serializer;
 
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer.ChoiceNodeBaseSerializer;
@@ -5,9 +5,10 @@
  * 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.json.schema.cnsn.serializer;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.serializer;
 
 import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
 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;
@@ -19,6 +20,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.FromNormalizedNodeSerializer;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.FromNormalizedNodeSerializerFactory;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer.NodeSerializerDispatcher;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
@@ -34,6 +36,7 @@ public final class CnSnFromNormalizedNodeSerializerFactory implements FromNormal
     private final MapNodeCnSnSerializer mapNodeSerializer;
     private final LeafSetEntryNodeCnSnSerializer leafSetEntryNodeSerializer;
     private final MapEntryNodeCnSnSerializer mapEntryNodeSerializer;
+    private final AnyXmlNodeCnSnSerializer anyXmlNodeSerializer;
 
     private CnSnFromNormalizedNodeSerializerFactory() {
         final NodeSerializerDispatcher.BaseNodeSerializerDispatcher<Node<?>> dispatcher = new NodeSerializerDispatcher.BaseNodeSerializerDispatcher<Node<?>>(
@@ -45,6 +48,7 @@ public final class CnSnFromNormalizedNodeSerializerFactory implements FromNormal
         choiceSerializer = new ChoiceNodeCnSnSerializer(dispatcher);
         augmentSerializer = new AugmentationNodeCnSnSerializer(dispatcher);
         leafNodeSerializer = new LeafNodeCnSnSerializer();
+        anyXmlNodeSerializer = new AnyXmlNodeCnSnSerializer();
 
         leafSetEntryNodeSerializer = new LeafSetEntryNodeCnSnSerializer();
         leafSetSerializer = new LeafSetNodeCnSnSerializer(leafSetEntryNodeSerializer);
@@ -99,4 +103,9 @@ public final class CnSnFromNormalizedNodeSerializerFactory implements FromNormal
     public FromNormalizedNodeSerializer<Node<?>, MapEntryNode, ListSchemaNode> getMapEntryNodeSerializer() {
         return mapEntryNodeSerializer;
     }
+
+    @Override
+    public FromNormalizedNodeSerializer<Node<?>, AnyXmlNode, AnyXmlSchemaNode> getAnyXmlNodeSerializer() {
+        return anyXmlNodeSerializer;
+    }
 }
@@ -5,20 +5,17 @@
  * 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.json.schema.cnsn.serializer;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.serializer;
 
 import com.google.common.base.Preconditions;
-
 import java.util.Collections;
 import java.util.List;
-
-import org.opendaylight.yangtools.yang.data.api.MutableCompositeNode;
-import org.opendaylight.yangtools.yang.data.api.MutableNode;
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer.ContainerNodeBaseSerializer;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer.NodeSerializerDispatcher;
+import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 
 public class ContainerNodeCnSnSerializer extends ContainerNodeBaseSerializer<Node<?>> {
@@ -30,19 +27,11 @@ public class ContainerNodeCnSnSerializer extends ContainerNodeBaseSerializer<Nod
     }
 
     @Override
-    public List<Node<?>> serialize(final ContainerSchemaNode schema, final ContainerNode containerNode) {
-
-        MutableCompositeNode mutCompNode = NodeFactory.createMutableCompositeNode(containerNode.getNodeType(), null,
-                null, null, null);
-
-        for (Node<?> element : super.serialize(schema, containerNode)) {
-            if (element instanceof MutableNode<?>) {
-                ((MutableNode<?>) element).setParent(mutCompNode);
-            }
-            mutCompNode.getValue().add(element);
-        }
-
-        return Collections.<Node<?>>singletonList(mutCompNode);
+    public List<Node<?>> serialize(final ContainerSchemaNode schema, final ContainerNode node) {
+        CompositeNodeBuilder<ImmutableCompositeNode> compNodeBuilder = ImmutableCompositeNode.builder();
+        compNodeBuilder.setQName(node.getNodeType());
+        compNodeBuilder.addAll(super.serialize(schema, node));
+        return Collections.<Node<?>> singletonList(compNodeBuilder.toInstance());        
     }
 
     @Override
@@ -5,7 +5,7 @@
  * 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.json.schema.cnsn.serializer;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.serializer;
 
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
@@ -5,7 +5,7 @@
  * 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.json.schema.cnsn.serializer;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.serializer;
 
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
@@ -5,7 +5,7 @@
  * 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.json.schema.cnsn.serializer;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.serializer;
 
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
@@ -5,20 +5,17 @@
  * 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.json.schema.cnsn.serializer;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.serializer;
 
 import com.google.common.base.Preconditions;
-
 import java.util.Collections;
 import java.util.List;
-
-import org.opendaylight.yangtools.yang.data.api.MutableCompositeNode;
-import org.opendaylight.yangtools.yang.data.api.MutableNode;
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
-import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer.MapEntryNodeBaseSerializer;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer.NodeSerializerDispatcher;
+import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
 
 public class MapEntryNodeCnSnSerializer extends MapEntryNodeBaseSerializer<Node<?>> {
@@ -30,19 +27,11 @@ public class MapEntryNodeCnSnSerializer extends MapEntryNodeBaseSerializer<Node<
     }
 
     @Override
-    public List<Node<?>> serialize(final ListSchemaNode schema, final MapEntryNode node) {
-
-        MutableCompositeNode mutCompNode = NodeFactory.createMutableCompositeNode(node.getNodeType(), null, null, null,
-                null);
-
-        for (Node<?> element : super.serialize(schema, node)) {
-            if (element instanceof MutableNode<?>) {
-                ((MutableNode<?>) element).setParent(mutCompNode);
-            }
-            mutCompNode.getValue().add(element);
-        }
-
-        return Collections.<Node<?>>singletonList(mutCompNode);
+    public List<Node<?>> serialize(ListSchemaNode schema, MapEntryNode node) {
+        CompositeNodeBuilder<ImmutableCompositeNode> compNodeBuilder = ImmutableCompositeNode.builder();
+        compNodeBuilder.setQName(node.getNodeType());
+        compNodeBuilder.addAll(super.serialize(schema, node));
+        return Collections.<Node<?>> singletonList(compNodeBuilder.toInstance());        
     }
 
     @Override
@@ -5,7 +5,7 @@
  * 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.json.schema.cnsn.serializer;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.serializer;
 
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
@@ -5,7 +5,7 @@
  * 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.json.schema.json;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.json;
 
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
similarity index 69%
rename from yang/yang-data-json/src/test/java/org/opendaylight/yangtools/yang/data/json/schema/TestUtils.java
rename to yang/yang-data-composite-node/src/test/java/org/opendaylight/yangtools/yang/data/composite/node/schema/TestUtils.java
index f6eb9a3cc540a23c0a2ebeb34d2edebf0be9b3d4..d2d2a3de3ac3bf5a926dd7edd27aeac45d97da97 100644 (file)
@@ -5,10 +5,9 @@
  * 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.json.schema;
+package org.opendaylight.yangtools.yang.data.composite.node.schema;
 
 import static org.junit.Assert.assertNotNull;
-import static org.opendaylight.yangtools.yang.data.impl.NodeFactory.createMutableCompositeNode;
 import static org.opendaylight.yangtools.yang.data.impl.NodeFactory.createMutableSimpleNode;
 
 import java.io.File;
@@ -25,24 +24,24 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 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.PathArgument;
-import org.opendaylight.yangtools.yang.data.api.MutableCompositeNode;
-import org.opendaylight.yangtools.yang.data.api.Node;
 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.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.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.DataContainerNodeBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.parser.api.YangModelParser;
@@ -125,69 +124,61 @@ public class TestUtils {
      * /cnsn-to-normalized-node/simple-conainer.json
      */
     public static CompositeNode prepareCompositeNodeStruct() {
-        MutableCompositeNode cont = createMutableCompositeNode(QName.create(MODULE_BASE, "cont"), null, null, null,
-                null);
+        CompositeNodeBuilder<ImmutableCompositeNode> contBuilder = ImmutableCompositeNode.builder();
+        contBuilder.setQName(QName.create(MODULE_BASE, "cont"));
 
         // cont1
-        List<Node<?>> contChilds = new ArrayList<>();
-        contChilds.add(createMutableCompositeNode(QName.create(MODULE_BASE, "cont1"),
-                cont,
-                Collections.<Node<?>> emptyList(), null, null));
+        contBuilder.add(ImmutableCompositeNode.builder().setQName(QName.create(MODULE_BASE, "cont1")).toInstance());
 
         // cont2
-        MutableCompositeNode cont2 = createMutableCompositeNode(QName.create(MODULE_BASE, "cont2"), cont, null, null,
-                null);
-        List<Node<?>> cont2Childs = new ArrayList<>();
-        cont2Childs.add(createMutableSimpleNode(QName.create(MODULE_BASE, "lf21"), cont2,
-                "value in cont2/lf21", null, null));
-        cont2.setValue(cont2Childs);
-        contChilds.add(cont2);
+        CompositeNodeBuilder<ImmutableCompositeNode> cont2 = ImmutableCompositeNode.builder().setQName(
+                QName.create(MODULE_BASE, "cont2"));
+        cont2.add(createMutableSimpleNode(QName.create(MODULE_BASE, "lf21"), null, "value in cont2/lf21", null, null));
+        contBuilder.add(cont2.toInstance());
 
         // lst1
-        contChilds.add(createMutableCompositeNode(QName.create(MODULE_BASE, "lst1"), cont,
-                Collections.<Node<?>> emptyList(), null, null));
+        contBuilder.add(ImmutableCompositeNode.builder().setQName(QName.create(MODULE_BASE, "lst1")).toInstance());
 
         // lst2
-        MutableCompositeNode lst2_1 = createMutableCompositeNode(QName.create(MODULE_BASE, "lst2"), cont, null, null,
-                null);
-        List<Node<?>> lst2_1Childs = new ArrayList<>();
-        lst2_1Childs
-        .add(createMutableSimpleNode(QName.create(MODULE_BASE, "lf21"),
-                lst2_1,
-                "some value21", null, null));
-        lst2_1.setValue(lst2_1Childs);
-        contChilds.add(lst2_1);
-
-        MutableCompositeNode lst2_2 = createMutableCompositeNode(QName.create(MODULE_BASE, "lst2"), cont, null, null,
-                null);
-        List<Node<?>> lst2_2Childs = new ArrayList<>();
-        lst2_2Childs
-        .add(createMutableSimpleNode(QName.create(MODULE_BASE, "lf22"), lst2_2, "some value22", null, null));
-        lst2_2.setValue(lst2_2Childs);
-        contChilds.add(lst2_2);
+        CompositeNodeBuilder<ImmutableCompositeNode> lst2_1Builder = ImmutableCompositeNode.builder().setQName(
+                QName.create(MODULE_BASE, "lst2"));
+        lst2_1Builder.add(createMutableSimpleNode(QName.create(MODULE_BASE, "lf21"), null, "some value21", null, null));
+        contBuilder.add(lst2_1Builder.toInstance());
+
+        CompositeNodeBuilder<ImmutableCompositeNode> lst2_2Builder = ImmutableCompositeNode.builder().setQName(
+                QName.create(MODULE_BASE, "lst2"));
+        lst2_2Builder.add(createMutableSimpleNode(QName.create(MODULE_BASE, "lf22"), null, "some value22", null, null));
+        contBuilder.add(lst2_2Builder.toInstance());
 
         // lflst1
-        contChilds.add(createMutableSimpleNode(QName.create(MODULE_BASE, "lflst1"), cont, "lflst1_1", null, null));
-        contChilds.add(createMutableSimpleNode(QName.create(MODULE_BASE, "lflst1"), cont, "lflst1_2", null, null));
+        contBuilder.add(createMutableSimpleNode(QName.create(MODULE_BASE, "lflst1"), null, "lflst1_1", null, null));
+        contBuilder.add(createMutableSimpleNode(QName.create(MODULE_BASE, "lflst1"), null, "lflst1_2", null, null));
 
         // lf1
-        contChilds.add(createMutableSimpleNode(QName.create(MODULE_BASE, "lf1"), cont, "lf1", null, null));
+        contBuilder.add(createMutableSimpleNode(QName.create(MODULE_BASE, "lf1"), null, "lf1", null, null));
 
         // lf11
-        contChilds.add(createMutableSimpleNode(QName.create(MODULE_BASE, "lf11"), cont, "value from case (cs1)", null,
+        contBuilder.add(createMutableSimpleNode(QName.create(MODULE_BASE, "lf11"), null, "value from case (cs1)", null,
                 null));
 
         // cont3
-        MutableCompositeNode cont3 = createMutableCompositeNode(QName.create(MODULE_AUGMENT, "cont3"), cont, null,
-                null, null);
-        List<Node<?>> cont3Childs = new ArrayList<>();
-        cont3Childs.add(createMutableSimpleNode(QName.create(MODULE_AUGMENT, "lf31"), cont3,
+        CompositeNodeBuilder<ImmutableCompositeNode> cont3Builder = ImmutableCompositeNode.builder().setQName(
+                QName.create(MODULE_AUGMENT, "cont3"));
+        cont3Builder.add(createMutableSimpleNode(QName.create(MODULE_AUGMENT, "lf31"), null,
                 "value in leaf in augment", null, null));
-        cont3.setValue(cont3Childs);
-        contChilds.add(cont3);
+        contBuilder.add(cont3Builder.toInstance());
+
+        // anxml-composite
+        CompositeNodeBuilder<ImmutableCompositeNode> anxmlCompositeBuilder = ImmutableCompositeNode.builder().setQName(
+                QName.create(MODULE_BASE, "anxml-composite"));
+        anxmlCompositeBuilder.add(NodeFactory.createImmutableSimpleNode(QName.create(MODULE_BASE, "anxml-cont"), null,
+                null));
+        contBuilder.add(anxmlCompositeBuilder.toInstance());
 
-        cont.setValue(contChilds);
-        return cont;
+        // anxml-simple
+        contBuilder.add(NodeFactory.createImmutableSimpleNode(QName.create(MODULE_BASE, "anxml-simple"), null,43));
+
+        return contBuilder.toInstance();
     }
 
     /**
@@ -203,7 +194,7 @@ public class TestUtils {
                 .withNodeIdentifier(getNodeIdentifier("cont2"))
                 .withChild(
                         Builders.leafBuilder().withNodeIdentifier(getNodeIdentifier("lf21"))
-                        .withValue("value in cont2/lf21").build()).build());
+                                .withValue("value in cont2/lf21").build()).build());
 
         CollectionNodeBuilder<MapEntryNode, MapNode> lst1 = Builders.mapBuilder().withNodeIdentifier(
                 getNodeIdentifier("lst1"));
@@ -249,7 +240,7 @@ public class TestUtils {
                 .withNodeIdentifier(getNodeIdentifier("chc"))
                 .withChild(
                         Builders.leafBuilder().withNodeIdentifier(getNodeIdentifier("lf11"))
-                        .withValue("value from case (cs1)").build()).build());
+                                .withValue("value from case (cs1)").build()).build());
 
         Set<QName> children = new HashSet<>();
         children.add(QName.create(MODULE_AUGMENT, "cont3"));
@@ -259,11 +250,31 @@ public class TestUtils {
                 .withNodeIdentifier(getAugmentationIdentifier(null, null, null, children))
                 .withChild(
                         Builders.containerBuilder()
-                        .withNodeIdentifier(getNodeIdentifier(MODULE_AUGMENT, "cont3"))
-                        .withChild(
-                                Builders.leafBuilder()
-                                .withNodeIdentifier(getNodeIdentifier(MODULE_AUGMENT, "lf31"))
-                                .withValue("value in leaf in augment").build()).build()).build());
+                                .withNodeIdentifier(getNodeIdentifier(MODULE_AUGMENT, "cont3"))
+                                .withChild(
+                                        Builders.leafBuilder()
+                                                .withNodeIdentifier(getNodeIdentifier(MODULE_AUGMENT, "lf31"))
+                                                .withValue("value in leaf in augment").build()).build()).build());
+
+        containerBuilder.withChild(Builders
+                .anyXmlBuilder()
+                .withNodeIdentifier(getNodeIdentifier("anxml-composite"))
+                .withValue(
+                        ImmutableCompositeNode
+                                .builder()
+                                .setQName(QName.create("simple:container:yang", "2013-11-12", "anxml-composite"))
+                                .add(NodeFactory.createImmutableSimpleNode(
+                                        QName.create("simple:container:yang", "2013-11-12", "anxml-cont"), null, null))
+                                .toInstance()).build());
+
+        containerBuilder
+                .withChild(Builders
+                        .anyXmlBuilder()
+                        .withNodeIdentifier(getNodeIdentifier("anxml-simple"))
+                        .withValue(
+                                NodeFactory.createImmutableSimpleNode(
+                                        QName.create("simple:container:yang", "2013-11-12", "anxml-simple"), null, 43))
+                        .build());
 
         ContainerNode build = containerBuilder.build();
         return build;
@@ -5,10 +5,15 @@
  * 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.json.schema.cnsn.parser;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.parser;
 
 import static org.junit.Assert.assertEquals;
 
+import org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.parser.CnSnToNormalizedNodeParserFactory;
+
+
+import org.opendaylight.yangtools.yang.data.composite.node.schema.TestUtils;
+
 import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.List;
@@ -19,7 +24,6 @@ import org.junit.Test;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-import org.opendaylight.yangtools.yang.data.json.schema.TestUtils;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.Module;
@@ -5,11 +5,16 @@
  * 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.json.schema.cnsn.serializer;
+package org.opendaylight.yangtools.yang.data.composite.node.schema.serializer;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
+import org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.serializer.CnSnFromNormalizedNodeSerializerFactory;
+
+
+import org.opendaylight.yangtools.yang.data.composite.node.schema.TestUtils;
+
 import java.net.URISyntaxException;
 import java.util.Set;
 
@@ -19,7 +24,6 @@ import org.junit.Test;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-import org.opendaylight.yangtools.yang.data.json.schema.TestUtils;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.Module;
similarity index 82%
rename from yang/yang-data-json/src/test/resources/cnsn-to-normalized-node/json/simple-container.json
rename to yang/yang-data-composite-node/src/test/resources/cnsn-to-normalized-node/json/simple-container.json
index b840efbd8f30e7881e5c1c9557952632eb8b9425..37dd7c0732177aa2001469715a2f046dcf996ad9 100644 (file)
         "lf1":"lf1",
         
         "lf11":"value from case (cs1)"
+        "anxml-composite": {            
+            "anxml-cont" {
+            }
+        }
+        
+        "anxml-simple":43;
     }
 }
\ No newline at end of file
index 02f020c47fdc98307eb4ad2cf9aff0621cfb9529..17d4e31919cb2d6302eb3a51beccaed821cc4173 100644 (file)
@@ -17,11 +17,8 @@ import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
 
 import java.net.URI;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.Map.Entry;
-import java.util.Set;
 
 import javax.activation.UnsupportedDataTypeException;
 import javax.xml.parsers.DocumentBuilder;
@@ -41,18 +38,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
 import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl;
 import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
-import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
-import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
-import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.api.SchemaNode;
-import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.*;
 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -62,6 +48,10 @@ import org.w3c.dom.Element;
 import org.w3c.dom.NodeList;
 
 public class XmlDocumentUtils {
+    private final static String RPC_REPLY_LOCAL_NAME = "rpc-reply";
+    private final static String RPC_REPLY_NAMESPACE = "urn:ietf:params:xml:ns:netconf:base:1.0";
+    private final static QName RPC_REPLY_QNAME = QName.create(URI.create(RPC_REPLY_NAMESPACE), null, RPC_REPLY_LOCAL_NAME);
+
     private static class ElementWithSchemaContext {
         Element element;
         SchemaContext schemaContext;
@@ -231,7 +221,7 @@ public class XmlDocumentUtils {
         }
         final TypeDefinition<?> baseType = XmlUtils.resolveBaseTypeFrom(schema.getType());
 
-        if (baseType instanceof org.opendaylight.yangtools.yang.model.util.InstanceIdentifier) {
+        if (baseType instanceof org.opendaylight.yangtools.yang.model.util.InstanceIdentifierType) {
             value = InstanceIdentifierForXmlCodec.deserialize(xmlElement,schemaCtx);
         } else if(baseType instanceof IdentityrefTypeDefinition){
             value = InstanceIdentifierForXmlCodec.toIdentity(xmlElement.getTextContent(), xmlElement, schemaCtx);
@@ -253,7 +243,7 @@ public class XmlDocumentUtils {
         if (codec != null) {
             value = codec.deserialize(text);
         }
-        if (schema.getType() instanceof org.opendaylight.yangtools.yang.model.util.InstanceIdentifier) {
+        if (schema.getType() instanceof org.opendaylight.yangtools.yang.model.util.InstanceIdentifierType) {
             value = InstanceIdentifierForXmlCodec.deserialize(xmlElement,schemaCtx);
         }
         if (value == null) {
@@ -404,6 +394,60 @@ public class XmlDocumentUtils {
         return null;
     }
 
+    /**
+     * Transforms XML Document representing Rpc output body into Composite Node structure based on Rpc definition
+     * within given Schema Context. The transformation is based on Rpc Definition which is defined in provided Schema Context.
+     * If Rpc Definition is missing from given Schema Context the method will return <code>null</code>
+     *
+     * @param document XML Document containing Output RPC message
+     * @param rpcName Rpc QName
+     * @param context Schema Context
+     * @return Rpc message in Composite Node data structures if Rpc definition is present within provided Schema Context, otherwise
+     * returns <code>null</code>
+     */
+    public static CompositeNode rpcReplyToDomNodes(final Document document, final QName rpcName,
+        final SchemaContext context) {
+        Preconditions.checkNotNull(document);
+        Preconditions.checkNotNull(rpcName);
+        Preconditions.checkNotNull(context);
+
+        Optional<RpcDefinition> rpcDefinition = findRpc(rpcName, context);
+        if (rpcDefinition.isPresent()) {
+             RpcDefinition rpc = rpcDefinition.get();
+
+            final Collection<DataSchemaNode> outputNode = rpc.getOutput().getChildNodes();
+            final Element rpcReplyElement = document.getDocumentElement();
+            final QName partialQName = qNameFromElement(rpcReplyElement);
+
+            if (RPC_REPLY_QNAME.equals(partialQName)) {
+                final List<Node<?>> domNodes = toDomNodes(rpcReplyElement, Optional.fromNullable(outputNode), context);
+                List<Node<?>> rpcOutNodes = Collections.<Node<?>>singletonList(ImmutableCompositeNode.create(
+                    rpc.getOutput().getQName(), domNodes));
+                return ImmutableCompositeNode.create(rpcName, rpcOutNodes);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Method searches given schema context for Rpc Definition with given QName.
+     * Returns Rpc Definition if is present within given Schema Context, otherwise returns Optional.absent().
+     *
+     * @param rpc Rpc QName
+     * @param context Schema Context
+     * @return Rpc Definition if is present within given Schema Context, otherwise returns Optional.absent().
+     */
+    private static Optional<RpcDefinition> findRpc(QName rpc, SchemaContext context) {
+        Preconditions.checkNotNull(rpc);
+        Preconditions.checkNotNull(context);
+        for (final RpcDefinition rpcDefinition : context.getOperations()) {
+            if ((rpcDefinition != null) && rpc.equals(rpcDefinition.getQName())) {
+                return Optional.of(rpcDefinition);
+            }
+        }
+        return Optional.absent();
+    }
+
     public static CompositeNode notificationToDomNodes(final Document document,
             final Optional<Set<NotificationDefinition>> notifications) {
         return notificationToDomNodes(document, notifications,null);
index 5d3fadb02a80b4a151dab62dcd06baeada4068cf..fc5c20e6fde66e21fa0c6e9159dfadbf0b1dfc80 100644 (file)
@@ -35,13 +35,32 @@ public final class ImmutableNodes {
         return ImmutableMapNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(name));
     }
 
-    public static final <T> LeafNode<T> leafNode(final QName name,final T value) {
+    /**
+     * Construct immutable leaf node
+     *
+     * @param name Identifier of leaf node
+     * @param value Value of leaf node
+     * @return Leaf node with supplied identifier and value
+     */
+    public static final <T> LeafNode<T> leafNode(final NodeIdentifier name,final T value) {
         return ImmutableLeafNodeBuilder.<T>create()
-                .withNodeIdentifier(new NodeIdentifier(name))
+                .withNodeIdentifier(name)
                 .withValue(value)
                 .build();
     }
 
+    /**
+     *
+     * Construct immutable leaf node
+     *
+     * @param name QName which will be used as node identifier
+     * @param value Value of leaf node.
+     * @return Leaf node with supplied identifier and value
+     */
+    public static final <T> LeafNode<T> leafNode(final QName name,final T value) {
+        return leafNode(new NodeIdentifier(name), value);
+    }
+
     public static DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder(final QName nodeName,final QName keyName,final Object keyValue) {
         return ImmutableMapEntryNodeBuilder.create()
                 .withNodeIdentifier(new NodeIdentifierWithPredicates(nodeName, keyName,keyValue))
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/ImmutableNormalizedNodeStreamWriter.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/ImmutableNormalizedNodeStreamWriter.java
new file mode 100644 (file)
index 0000000..21fda47
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * 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.impl.schema;
+
+import com.google.common.base.Preconditions;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.List;
+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.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+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.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableAugmentationNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUnkeyedListNodeBuilder;
+
+/**
+ *
+ * Implementation of {@link NormalizedNodeStreamWriter}, which constructs
+ * immutable instances of {@link NormalizedNode}s.
+ * <p>
+ * This writer supports two modes of behaviour one is using {@link #from(NormalizedNodeResult)}
+ * where resulting NormalizedNode will be stored in supplied result object.
+ *
+ * Other mode of operation is using {@link #from(NormalizedNodeContainerBuilder)},
+ * where all created nodes will be written to this builder.
+ *
+ *
+ */
+public class ImmutableNormalizedNodeStreamWriter implements NormalizedNodeStreamWriter {
+
+
+
+    @SuppressWarnings("rawtypes")
+    private final Deque<NormalizedNodeContainerBuilder> builders;
+
+
+    @SuppressWarnings("rawtypes")
+    private ImmutableNormalizedNodeStreamWriter( final NormalizedNodeContainerBuilder topLevelBuilder) {
+        builders = new ArrayDeque<>();
+        builders.push(topLevelBuilder);
+    }
+
+    /**
+     * Creates a {@link NormalizedNodeStreamWriter} which creates instances of supplied
+     * {@link NormalizedNode}s and writes them to supplied builder as child nodes.
+     * <p>
+     * Type of supplied {@link NormalizedNodeContainerBuilder} affects,
+     * which events could be emitted in order to ensure proper construction of
+     * data.
+     *
+     * @param builder Builder to which data will be written.
+     * @return {@link NormalizedNodeStreamWriter} which writes data
+     */
+    public static final NormalizedNodeStreamWriter from(final NormalizedNodeContainerBuilder<?, ?, ?, ?> builder) {
+        return new ImmutableNormalizedNodeStreamWriter(builder);
+    }
+
+    /**
+     *
+     * Creates a {@link NormalizedNodeStreamWriter} which creates one instance of top
+     * level {@link NormalizedNode} (type of NormalizedNode) is determined by first
+     * start event.
+     * <p>
+     * Result is built when {@link #endNode()} associated with that start event
+     * is emitted.
+     * <p>
+     * Writer properly creates also nested {@link NormalizedNode} instances,
+     * if their are supported inside the scope of first event.
+     * <p>
+     * This method is useful for clients, which knows there will be one
+     * top level node written, but does not know which type of {@link NormalizedNode}
+     * will be writen.
+     *
+     *
+     * @param result {@link NormalizedNodeResult} object which will hold result value.
+     * @return {@link NormalizedNodeStreamWriter} whcih will write item to supplied result holder.
+     */
+    public static final NormalizedNodeStreamWriter from(final NormalizedNodeResult result) {
+        return new ImmutableNormalizedNodeStreamWriter(new NormalizedNodeResultBuilder(result));
+    }
+
+
+    @SuppressWarnings("rawtypes")
+    private NormalizedNodeContainerBuilder getCurrent() {
+        return builders.peek();
+    }
+
+    @SuppressWarnings("rawtypes")
+    private void enter(final NormalizedNodeContainerBuilder next) {
+        builders.push(next);
+    }
+
+    @SuppressWarnings("unchecked")
+    private void writeChild(final NormalizedNode<?, ?> child) {
+        getCurrent().addChild(child);
+    }
+
+    @Override
+    @SuppressWarnings({"rawtypes","unchecked"})
+    public void endNode() {
+        final NormalizedNodeContainerBuilder finishedBuilder = builders.poll();
+        Preconditions.checkState(finishedBuilder != null, "Node which should be closed does not exists.");
+        NormalizedNodeContainerBuilder current = getCurrent();
+        Preconditions.checkState(current != null, "Reached top level node, which could not be closed in this writer.");
+        NormalizedNode<PathArgument, ?> product = finishedBuilder.build();
+        current.addChild(product);
+    }
+
+    @Override
+    public void leafNode(final NodeIdentifier name, final Object value) throws IllegalArgumentException {
+        checkDataNodeContainer();
+        writeChild(ImmutableNodes.leafNode(name, value));
+    }
+
+    @Override
+    public void startLeafSet(final NodeIdentifier name,final int childSizeHint) throws IllegalArgumentException {
+        checkDataNodeContainer();
+        ListNodeBuilder<Object, LeafSetEntryNode<Object>> builder = Builders.leafSetBuilder();
+        builder.withNodeIdentifier(name);
+        enter(builder);
+    }
+
+    @Override
+    public void leafSetEntryNode(final Object value) throws IllegalArgumentException {
+        Preconditions.checkArgument(getCurrent() instanceof ImmutableLeafSetNodeBuilder<?>);
+        @SuppressWarnings("unchecked")
+        ListNodeBuilder<Object, LeafSetEntryNode<Object>> builder = ((ImmutableLeafSetNodeBuilder<Object>) getCurrent());
+        builder.withChildValue(value);
+    }
+
+    @Override
+    public void anyxmlNode(final NodeIdentifier name, final Object value) throws IllegalArgumentException {
+        checkDataNodeContainer();
+
+
+    }
+
+    @Override
+    public void startContainerNode(final NodeIdentifier name,final int childSizeHint) throws IllegalArgumentException {
+        checkDataNodeContainer();
+        enter(Builders.containerBuilder().withNodeIdentifier(name));
+    }
+
+    @Override
+    public void startUnkeyedList(final NodeIdentifier name,final int childSizeHint) throws IllegalArgumentException {
+        checkDataNodeContainer();
+        enter(Builders.unkeyedListBuilder().withNodeIdentifier(name));
+    }
+
+    @Override
+    public void startUnkeyedListItem(final NodeIdentifier name,final int childSizeHint) throws IllegalStateException {
+        Preconditions.checkArgument(getCurrent() instanceof ImmutableUnkeyedListNodeBuilder);
+        enter(Builders.unkeyedListEntryBuilder().withNodeIdentifier(name));
+    }
+
+    @Override
+    public void startMapNode(final NodeIdentifier name,final int childSizeHint) throws IllegalArgumentException {
+        checkDataNodeContainer();
+        enter(Builders.mapBuilder().withNodeIdentifier(name));
+    }
+
+    @Override
+    public void startMapEntryNode(final NodeIdentifierWithPredicates identifier,final int childSizeHint) throws IllegalArgumentException {
+        Preconditions.checkArgument(getCurrent() instanceof ImmutableMapNodeBuilder);
+        enter(Builders.mapEntryBuilder().withNodeIdentifier(identifier));
+    }
+
+    @Override
+    public void startOrderedMapNode(final NodeIdentifier name,final int childSizeHint) throws IllegalArgumentException {
+        checkDataNodeContainer();
+        enter(Builders.mapBuilder().withNodeIdentifier(name));
+    }
+
+    @Override
+    public void startChoiceNode(final NodeIdentifier name,final int childSizeHint) throws IllegalArgumentException {
+        checkDataNodeContainer();
+        enter(Builders.choiceBuilder().withNodeIdentifier(name));
+    }
+    @Override
+    public void startAugmentationNode(final AugmentationIdentifier identifier) throws IllegalArgumentException {
+        checkDataNodeContainer();
+        Preconditions.checkArgument(!(getCurrent() instanceof ImmutableAugmentationNodeBuilder));
+        enter(Builders.augmentationBuilder().withNodeIdentifier(identifier));
+    }
+
+    private void checkDataNodeContainer() {
+        @SuppressWarnings("rawtypes")
+        NormalizedNodeContainerBuilder current = getCurrent();
+        if(!(current instanceof NormalizedNodeResultBuilder)) {
+        Preconditions.checkArgument(current instanceof DataContainerNodeBuilder<?, ?>, "Invalid nesting of data.");
+        }
+    }
+
+    @SuppressWarnings("rawtypes")
+    private static final class NormalizedNodeResultBuilder implements NormalizedNodeContainerBuilder {
+
+        private final NormalizedNodeResult result;
+
+        public NormalizedNodeResultBuilder(final NormalizedNodeResult result) {
+            this.result = result;
+        }
+
+        @Override
+        public NormalizedNodeBuilder withValue(final Object value) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public NormalizedNode build() {
+            throw new IllegalStateException("Can not close NormalizedNodeResult");
+        }
+
+        @Override
+        public NormalizedNodeContainerBuilder withNodeIdentifier(final PathArgument nodeIdentifier) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public NormalizedNodeContainerBuilder withValue(final List value) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public NormalizedNodeContainerBuilder addChild(final NormalizedNode child) {
+            result.setResult(child);
+            return this;
+        }
+
+        @Override
+        public NormalizedNodeContainerBuilder removeChild(final PathArgument key) {
+            throw new UnsupportedOperationException();
+        }
+
+    }
+
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/NormalizedNodeResult.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/NormalizedNodeResult.java
new file mode 100644 (file)
index 0000000..76b28e9
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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.impl.schema;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Client allocated result holder for {@link ImmutableNormalizedNodeStreamWriter}.
+ * which produces instance of NormalizedNode.
+ *
+ * Client may supply result holder to {@link ImmutableNormalizedNodeStreamWriter}
+ * which will be once updated, when result is available.
+ *
+ * This is intended for using {@link ImmutableNormalizedNodeStreamWriter}
+ * without supplying builder, so instantiated writer will select
+ * correct builder based on first event and sets resulting
+ *  {@link NormalizedNode} when end event is invoked for node.
+ *
+ */
+public class NormalizedNodeResult {
+
+    private boolean finished = false;
+    private NormalizedNode<?,?> result;
+
+    public NormalizedNode<?, ?> getResult() {
+        return result;
+    }
+
+    void setResult(final NormalizedNode<?, ?> result) {
+        Preconditions.checkState(!this.finished, "Result was already set.");
+        this.finished = true;
+        this.result = result;
+    }
+
+    public boolean isFinished() {
+        return finished;
+    }
+
+}
index 48fedda8606869730fdcf8ee339b13c41c9eb4ed..319752d282bf61a3b8ae43ef6e42eb8701b7d37e 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.yangtools.yang.data.impl.schema.transform;
 
+import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
 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;
@@ -15,6 +16,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
@@ -36,4 +38,5 @@ public interface FromNormalizedNodeSerializerFactory<E> {
        FromNormalizedNodeSerializer<E, LeafSetNode<?>, LeafListSchemaNode> getLeafSetNodeSerializer();
        FromNormalizedNodeSerializer<E, MapEntryNode, ListSchemaNode> getMapEntryNodeSerializer();
        FromNormalizedNodeSerializer<E, MapNode, ListSchemaNode> getMapNodeSerializer();
+       FromNormalizedNodeSerializer<E, AnyXmlNode, AnyXmlSchemaNode> getAnyXmlNodeSerializer();
 }
index f99cbcff281d8b708d2a232d6a083fde21bb2b6a..7c2700f7c4139f83208845e570c194b1d445a5aa 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.yangtools.yang.data.impl.schema.transform;
 
+import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
 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;
@@ -15,6 +16,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
@@ -36,4 +38,5 @@ public interface ToNormalizedNodeParserFactory<E> {
        ToNormalizedNodeParser<E, LeafSetNode<?>, LeafListSchemaNode> getLeafSetNodeParser();
        ToNormalizedNodeParser<E, MapEntryNode, ListSchemaNode> getMapEntryNodeParser();
        ToNormalizedNodeParser<E, MapNode, ListSchemaNode> getMapNodeParser();
+        ToNormalizedNodeParser<E, AnyXmlNode, AnyXmlSchemaNode> getAnyXmlNodeParser();
 }
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/transform/base/parser/AnyXmlNodeBaseParser.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/transform/base/parser/AnyXmlNodeBaseParser.java
new file mode 100644 (file)
index 0000000..2730367
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * 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
+ */
+package org.opendaylight.yangtools.yang.data.impl.schema.transform.base.parser;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Iterables;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.ToNormalizedNodeParser;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
+
+/**
+ * Abstract(base) parser for LeafNodes, parses elements of type E.
+ *
+ * @param <E> type of elements to be parsed
+ */
+public abstract class AnyXmlNodeBaseParser<E> implements
+        ToNormalizedNodeParser<E, AnyXmlNode, AnyXmlSchemaNode> {
+
+    @Override
+    public final AnyXmlNode parse(Iterable<E> elements, AnyXmlSchemaNode schema) {
+        final int size = Iterables.size(elements);
+        Preconditions.checkArgument(size == 1, "Elements mapped to any-xml node illegal count: %s", size);
+
+        final E e = elements.iterator().next();
+        Node<?> value = parseAnyXml(e, schema);
+
+        NormalizedNodeAttrBuilder<NodeIdentifier, Node<?>, AnyXmlNode> anyXmlBuilder = Builders.anyXmlBuilder(schema);
+
+        return anyXmlBuilder.withValue(value).build();
+    }
+
+    /**
+     *
+     * Parse the inner value of a AnyXmlNode from element of type E.
+     *
+     * @param element to be parsed
+     * @param schema schema for leaf
+     * @return parsed element as an Object
+     */
+    protected abstract Node<?> parseAnyXml(E element, AnyXmlSchemaNode schema);
+
+}
index 40722b3aee8a15c05f37c9974e475f7fc67d5478..c316e72b02a59e3fe2a1aaf4441efca4c43cf80a 100644 (file)
@@ -7,10 +7,11 @@
  */
 package org.opendaylight.yangtools.yang.data.impl.schema.transform.base.parser;
 
+import com.google.common.base.Preconditions;
 import java.util.List;
-
 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.ToNormalizedNodeParserFactory;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
@@ -18,8 +19,6 @@ import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
 
-import com.google.common.base.Preconditions;
-
 /**
  *
  * Dispatches the parsing process of elements according to schema and returns the parsed Node.
@@ -57,6 +56,8 @@ public interface NodeParserDispatcher<E> {
                 return factory.getChoiceNodeParser().parse(childNodes, (ChoiceNode) schema);
             } else if (schema instanceof AugmentationSchema) {
                 return factory.getAugmentationNodeParser().parse(childNodes, (AugmentationSchema) schema);
+            } else if (schema instanceof AnyXmlSchemaNode) {
+                return factory.getAnyXmlNodeParser().parse(childNodes,(AnyXmlSchemaNode)schema);
             }
 
             throw new IllegalArgumentException("Unable to parse node, unknown schema type: " + schema.getClass());
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/transform/base/serializer/AnyXmlNodeBaseSerializer.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/transform/base/serializer/AnyXmlNodeBaseSerializer.java
new file mode 100644 (file)
index 0000000..e2fd60b
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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
+ */
+package org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer;
+
+import java.util.Collections;
+import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.FromNormalizedNodeSerializer;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
+
+/**
+ * Abstract(base) serializer for AnyXmlNodes, serializes elements of type E.
+ *
+ * @param <E> type of serialized elements
+ */
+public abstract class AnyXmlNodeBaseSerializer<E> implements
+        FromNormalizedNodeSerializer<E, AnyXmlNode, AnyXmlSchemaNode> {
+
+    @Override
+    public final Iterable<E> serialize(AnyXmlSchemaNode schema, AnyXmlNode node) {
+        return Collections.singletonList(serializeAnyXml(node));
+    }
+
+    /**
+     *
+     * Serialize the inner value of a AnyXmlNode into element of type E.
+     *
+     * @param node to be serialized
+     * @param schema schema for leaf
+     * @return serialized inner value as an Element
+     */
+    protected abstract E serializeAnyXml(AnyXmlNode node);
+}
index d08a9fbf8d713ad31fd4a83aea1e69639992943a..c686c31cba4871577c415937262c62b00c5046fd 100644 (file)
@@ -7,7 +7,10 @@
  */
 package org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer;
 
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Iterables;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
 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;
@@ -17,20 +20,20 @@ import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MixinNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.FromNormalizedNodeSerializerFactory;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
 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.ListSchemaNode;
 
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Iterables;
-
 /**
  *
- * Dispatches the serialization process of nodes according to schema and returns the serialized elements.
+ * Dispatches the serialization process of nodes according to schema and returns
+ * the serialized elements.
  *
- * @param <E> type of serialized elements
+ * @param <E>
+ *            type of serialized elements
  */
 public interface NodeSerializerDispatcher<E> {
 
@@ -38,8 +41,9 @@ public interface NodeSerializerDispatcher<E> {
             DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> dataContainerChild);
 
     /**
-     * Abstract implementation that implements the dispatch conditions. Only requires serializers to be provided.
-     * The same instance of serializer can be provided in case it is immutable.
+     * Abstract implementation that implements the dispatch conditions. Only
+     * requires serializers to be provided. The same instance of serializer can
+     * be provided in case it is immutable.
      */
     public static abstract class BaseNodeSerializerDispatcher<E> implements NodeSerializerDispatcher<E> {
         private final FromNormalizedNodeSerializerFactory<E> factory;
@@ -55,6 +59,8 @@ public interface NodeSerializerDispatcher<E> {
                 return onContainerNode(childSchema, dataContainerChild);
             } else if (dataContainerChild instanceof LeafNode<?>) {
                 return onLeafNode(childSchema, dataContainerChild);
+            } else if (dataContainerChild instanceof AnyXmlNode) {
+                return onAnyXmlNode(childSchema, dataContainerChild);
             } else if (dataContainerChild instanceof MixinNode) {
                 if (dataContainerChild instanceof LeafSetNode<?>) {
                     return onLeafListNode(childSchema, dataContainerChild);
@@ -107,6 +113,15 @@ public interface NodeSerializerDispatcher<E> {
             return elements;
         }
 
+        private Iterable<E> onAnyXmlNode(Object childSchema,
+                DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> dataContainerChild) {
+            checkSchemaCompatibility(childSchema, AnyXmlSchemaNode.class, dataContainerChild);
+            Iterable<E> elements = factory.getAnyXmlNodeSerializer().serialize((AnyXmlSchemaNode) childSchema,
+                    (AnyXmlNode) dataContainerChild);
+            checkOnlyOneSerializedElement(elements, dataContainerChild);
+            return elements;
+        }
+
         private static void checkOnlyOneSerializedElement(Iterable<?> elements,
                 DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> dataContainerChild) {
             final int size = Iterables.size(elements);
index 53d68f887267719865f9be0b50bb8e907c479533..a02b60b2fd24b6443596e3dd4fe4e508433b0518 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser;
 
+import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
 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;
@@ -19,6 +20,7 @@ import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlCodecProvider;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.ToNormalizedNodeParser;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.ToNormalizedNodeParserFactory;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.parser.NodeParserDispatcher;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
@@ -95,4 +97,9 @@ public final class DomToNormalizedNodeParserFactory implements ToNormalizedNodeP
     public ToNormalizedNodeParser<Element, MapNode, ListSchemaNode> getMapNodeParser() {
         return mapNodeParser;
     }
+
+    @Override
+    public ToNormalizedNodeParser<Element, AnyXmlNode, AnyXmlSchemaNode> getAnyXmlNodeParser() {
+        throw new UnsupportedOperationException();
+    }
 }
index 0a5aba7c4a67789935bd98770d85e07e2cb1d8c6..b9d8ef2a757a3dec9cd0f3faf00d2194fe02a24a 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.serializer;
 
+import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
 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;
@@ -19,6 +20,7 @@ import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlCodecProvider;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.FromNormalizedNodeSerializer;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.FromNormalizedNodeSerializerFactory;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer.NodeSerializerDispatcher;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
@@ -98,4 +100,9 @@ public final class DomFromNormalizedNodeSerializerFactory implements FromNormali
                return mapNodeSerializer;
        }
 
+        @Override
+        public FromNormalizedNodeSerializer<Element, AnyXmlNode, AnyXmlSchemaNode> getAnyXmlNodeSerializer() {
+            throw new UnsupportedOperationException();
+        }
+
 }
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/test/RpcReplyToDomTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/test/RpcReplyToDomTest.java
new file mode 100644 (file)
index 0000000..e7a4261
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * 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
+ */
+package org.opendaylight.yangtools.yang.data.codec.xml.test;
+
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import org.junit.Before;
+import org.junit.Test;
+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.impl.codec.xml.XmlDocumentUtils;
+import org.opendaylight.yangtools.yang.model.api.Module;
+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.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+/**
+ *
+ * @author Lukas Sedlak <lsedlak@cisco.com>
+ */
+public class RpcReplyToDomTest {
+
+    private static final DocumentBuilderFactory BUILDER_FACTORY;
+    private static final String RES_SCHEMA_DIR = "/org/opendaylight/yangtools/yang/data/impl/schema";
+
+    private final static String MODEL_NAMESPACE = "org:opendaylight:rpc-reply:test:ns:yang";
+    private final static String MODEL_REVISION = "2014-07-17";
+
+    private final static QName RPC_OUTPUT_QNAME = QName.create(MODEL_NAMESPACE, MODEL_REVISION, "output");
+    private final static QName ROCK_THE_HOUSE_QNAME = QName.create(MODEL_NAMESPACE, MODEL_REVISION, "rock-the-house");
+    private final static QName ACTIV_SW_IMAGE_QNAME = QName.create(MODEL_NAMESPACE, MODEL_REVISION, "activate-software-image");
+
+    private SchemaContext schemaContext;
+    private Document testPayload1;
+    private Document testPayload2;
+
+    static {
+        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        factory.setNamespaceAware(true);
+        factory.setCoalescing(true);
+        factory.setIgnoringElementContentWhitespace(true);
+        factory.setIgnoringComments(true);
+        BUILDER_FACTORY = factory;
+    }
+
+    @Before
+    public void setup() throws Exception {
+        final List<InputStream> modelsToParse = Collections
+            .singletonList(getClass().getResourceAsStream(RES_SCHEMA_DIR + "/rpc-test-model.yang"));
+        assertNotNull(modelsToParse);
+        assertNotNull(modelsToParse.get(0));
+
+        final YangContextParser parser = new YangParserImpl();
+        final Set<Module> modules = parser.parseYangModelsFromStreams(modelsToParse);
+        assertTrue(!modules.isEmpty());
+        schemaContext = parser.resolveSchemaContext(modules);
+        assertNotNull(schemaContext);
+
+        final InputStream rpcPayloadStream1 = getClass().getResourceAsStream(RES_SCHEMA_DIR + "/rpc-test-payload1.xml");
+        InputStream rpcPayloadStream2 = getClass().getResourceAsStream(RES_SCHEMA_DIR + "/rpc-test-payload2.xml");
+
+        assertNotNull(rpcPayloadStream1);
+        assertNotNull(rpcPayloadStream2);
+
+        testPayload1 = readXmlToDocument(rpcPayloadStream1);
+        testPayload2 = readXmlToDocument(rpcPayloadStream2);
+
+        assertNotNull(testPayload1);
+        assertNotNull(testPayload2);
+    }
+
+    private static Document readXmlToDocument(InputStream xmlContent) throws SAXException, IOException {
+        DocumentBuilder dBuilder;
+        try {
+            dBuilder = BUILDER_FACTORY.newDocumentBuilder();
+        } catch (ParserConfigurationException e) {
+            throw new IllegalStateException("Failed to parse XML document", e);
+        }
+        Document doc = dBuilder.parse(xmlContent);
+
+        doc.getDocumentElement().normalize();
+        return doc;
+    }
+
+    @Test
+    public void test() {
+        final CompositeNode rockNode = XmlDocumentUtils
+            .rpcReplyToDomNodes(testPayload1, ROCK_THE_HOUSE_QNAME, schemaContext);
+        assertNotNull(rockNode);
+
+        final String namespace = "org:opendaylight:rpc-reply:test:ns:yang";
+        final String revision = "2014-07-17";
+
+        CompositeNode output = rockNode.getFirstCompositeByName(RPC_OUTPUT_QNAME);
+        assertNotNull(output);
+
+        final SimpleNode<?> zipCode = output.getFirstSimpleByName(
+            QName.create(namespace, revision, "zip-code"));
+        assertNotNull(zipCode);
+
+        final CompositeNode activNode = XmlDocumentUtils
+            .rpcReplyToDomNodes(testPayload2, ACTIV_SW_IMAGE_QNAME, schemaContext);
+        assertNotNull(activNode);
+
+        output = activNode.getFirstCompositeByName(RPC_OUTPUT_QNAME);
+        assertNotNull(output);
+
+        final CompositeNode imgProps = output
+            .getFirstCompositeByName(QName.create(namespace, revision, "image-properties"));
+        assertNotNull(imgProps);
+        final CompositeNode imgProperty = imgProps
+            .getFirstCompositeByName(QName.create(namespace, revision, "image-property"));
+        assertNotNull(imgProperty);
+
+        final SimpleNode<?> imgId = imgProperty
+            .getFirstSimpleByName(QName.create(namespace, revision, "image-id"));
+        assertNotNull(imgId);
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/schema/rpc-test-model.yang b/yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/schema/rpc-test-model.yang
new file mode 100644 (file)
index 0000000..652b6a0
--- /dev/null
@@ -0,0 +1,44 @@
+module rpc-test-model {
+yang-version 1;
+    namespace "org:opendaylight:rpc-reply:test:ns:yang";
+    prefix "user";
+
+    organization "Cisco Systems";
+    contact "Lukas Sedlak";
+    description "Test model for testing rpc input message translation to DOM Nodes.";
+
+    revision "2014-07-17" {
+        description "Initial revision";
+    }
+
+    rpc rock-the-house {
+        output {
+            leaf zip-code {
+                type string;
+            }
+        }
+    }
+
+    rpc activate-software-image {
+        input {
+            leaf image-name {
+                type string;
+            }
+        }
+        output {
+            container image-properties {
+                list image-property {
+                    key "image-id";
+
+                    leaf image-id {
+                        type string;
+                    }
+                }
+            }
+
+            leaf status {
+                type string;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/schema/rpc-test-payload1.xml b/yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/schema/rpc-test-payload1.xml
new file mode 100644 (file)
index 0000000..d81377a
--- /dev/null
@@ -0,0 +1,4 @@
+<rpc-reply message-id="101"
+           xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+  <trpc:zip-code xmlns:trpc="org:opendaylight:rpc-reply:test:ns:yang">123014</trpc:zip-code>
+</rpc-reply>
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/schema/rpc-test-payload2.xml b/yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/schema/rpc-test-payload2.xml
new file mode 100644 (file)
index 0000000..79792cc
--- /dev/null
@@ -0,0 +1,12 @@
+<rpc-reply message-id="101"
+           xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"
+           xmlns:rtm="org:opendaylight:rpc-reply:test:ns:yang"
+           rtm:image-name="img.img">
+
+  <image-properties xmlns="org:opendaylight:rpc-reply:test:ns:yang">
+    <image-property>
+      <image-id>id12345_test_data</image-id>
+    </image-property>
+  </image-properties>
+  <status xmlns="org:opendaylight:rpc-reply:test:ns:yang">testing_data</status>
+</rpc-reply>
\ No newline at end of file
index 28bb2d9b70fae8919781070080404200e6a9e308..dc2cbd1e676460ec7138752c2b1d0716637b7089 100644 (file)
@@ -11,7 +11,7 @@
     <parent>
         <groupId>org.opendaylight.yangtools</groupId>
         <artifactId>test-parent</artifactId>
-        <version>1.0</version>
+        <version>0.6.2-SNAPSHOT</version>
     </parent>
 
     <artifactId>additional-config</artifactId>
@@ -20,7 +20,7 @@
         <dependency>
             <groupId>org.opendaylight.yangtools</groupId>
             <artifactId>yang-maven-plugin-spi</artifactId>
-            <version>${it-project.version}</version>
+            <version>${project.version}</version>
         </dependency>
     </dependencies>
 
@@ -29,7 +29,7 @@
             <plugin>
                 <groupId>org.opendaylight.yangtools</groupId>
                 <artifactId>yang-maven-plugin</artifactId>
-                <version>${it-project.version}</version>
+                <version>${project.version}</version>
                 <executions>
                     <execution>
                         <goals>
@@ -71,7 +71,7 @@
                     <dependency>
                         <groupId>org.opendaylight.yangtools</groupId>
                         <artifactId>yang-maven-plugin-spi</artifactId>
-                        <version>${it-project.version}</version>
+                        <version>${project.version}</version>
                         <type>test-jar</type>
                     </dependency>
                 </dependencies>
index 82640e8ee674c05da066fd37e43e607cef2ab3c2..d99dd0319a341fe919aef2f8c360f1bf7b25824a 100644 (file)
@@ -11,7 +11,7 @@
     <parent>
         <groupId>org.opendaylight.yangtools</groupId>
         <artifactId>test-parent</artifactId>
-        <version>1.0</version>
+        <version>0.6.2-SNAPSHOT</version>
     </parent>
 
     <artifactId>correct</artifactId>
@@ -20,7 +20,7 @@
         <dependency>
             <groupId>org.opendaylight.yangtools</groupId>
             <artifactId>yang-maven-plugin-spi</artifactId>
-            <version>${it-project.version}</version>
+            <version>${project.version}</version>
         </dependency>
     </dependencies>
 
@@ -29,7 +29,7 @@
             <plugin>
                 <groupId>org.opendaylight.yangtools</groupId>
                 <artifactId>yang-maven-plugin</artifactId>
-                <version>${it-project.version}</version>
+                <version>${project.version}</version>
                 <executions>
                     <execution>
                         <goals>
@@ -56,7 +56,7 @@
                     <dependency>
                         <groupId>org.opendaylight.yangtools</groupId>
                         <artifactId>yang-maven-plugin-spi</artifactId>
-                        <version>${it-project.version}</version>
+                        <version>${project.version}</version>
                         <type>test-jar</type>
                     </dependency>
                 </dependencies>
index cd7be965d193bb026422989970260e4dc81d5068..3d931010a355f00051f96274b3755acf487e9289 100644 (file)
@@ -11,7 +11,7 @@
     <parent>
         <groupId>org.opendaylight.yangtools</groupId>
         <artifactId>test-parent</artifactId>
-        <version>1.0</version>
+        <version>0.6.2-SNAPSHOT</version>
     </parent>
 
     <artifactId>generator-test1</artifactId>
@@ -25,7 +25,7 @@
             <plugin>
                 <groupId>org.opendaylight.yangtools</groupId>
                 <artifactId>yang-maven-plugin</artifactId>
-                <version>${it-project.version}</version>
+                <version>${project.version}</version>
                 <executions>
                     <execution>
                         <goals>
@@ -51,7 +51,7 @@
                     <dependency>
                         <groupId>org.opendaylight.yangtools</groupId>
                         <artifactId>yang-maven-plugin-spi</artifactId>
-                        <version>${it-project.version}</version>
+                        <version>${project.version}</version>
                         <type>test-jar</type>
                     </dependency>
                 </dependencies>
index 948c025898f9c0aa7b3040fe1a43fa2aac5e1e59..3063698e67e7b56a1263bf9e0ca093c966029905 100644 (file)
@@ -11,7 +11,7 @@
     <parent>
         <groupId>org.opendaylight.yangtools</groupId>
         <artifactId>test-parent</artifactId>
-        <version>1.0</version>
+        <version>0.6.2-SNAPSHOT</version>
     </parent>
 
     <artifactId>generator-test2</artifactId>
@@ -24,9 +24,9 @@
         <dependency>
             <groupId>org.opendaylight.yangtools</groupId>
             <artifactId>generator-test1</artifactId>
-            <version>${it-project.version}</version>
+            <version>${project.version}</version>
             <scope>system</scope>
-            <systemPath>${project.basedir}/../GenerateTest1/target/generator-test1-1.0.jar</systemPath>
+            <systemPath>${project.basedir}/../GenerateTest1/target/generator-test1-${project.version}.jar</systemPath>
         </dependency>
     </dependencies>
 
@@ -35,7 +35,7 @@
             <plugin>
                 <groupId>org.opendaylight.yangtools</groupId>
                 <artifactId>yang-maven-plugin</artifactId>
-                <version>${it-project.version}</version>
+                <version>${project.version}</version>
                 <executions>
                     <execution>
                         <goals>
@@ -61,7 +61,7 @@
                     <dependency>
                         <groupId>org.opendaylight.yangtools</groupId>
                         <artifactId>yang-maven-plugin-spi</artifactId>
-                        <version>${it-project.version}</version>
+                        <version>${project.version}</version>
                         <type>test-jar</type>
                     </dependency>
                 </dependencies>
index be217f8d6255961d34c8fa3ffcadb382a22d2461..c764eca085f5d39f41422a4206abce176417e671 100644 (file)
@@ -11,7 +11,7 @@
     <parent>
         <groupId>org.opendaylight.yangtools</groupId>
         <artifactId>test-parent</artifactId>
-        <version>1.0</version>
+        <version>0.6.2-SNAPSHOT</version>
     </parent>
 
     <artifactId>generator</artifactId>
@@ -21,7 +21,7 @@
             <plugin>
                 <groupId>org.opendaylight.yangtools</groupId>
                 <artifactId>yang-maven-plugin</artifactId>
-                <version>${it-project.version}</version>
+                <version>${project.version}</version>
                 <executions>
                     <execution>
                         <goals>
@@ -48,7 +48,7 @@
                     <dependency>
                         <groupId>org.opendaylight.yangtools</groupId>
                         <artifactId>yang-maven-plugin-spi</artifactId>
-                        <version>${it-project.version}</version>
+                        <version>${project.version}</version>
                         <type>test-jar</type>
                     </dependency>
                 </dependencies>
index 8c45963a8d63ded42e593aa1b9a3c5431ca4b6a8..2e9982c5dd8bd423de49401ce6c27632d40eb14c 100644 (file)
@@ -11,7 +11,7 @@
     <parent>
         <groupId>org.opendaylight.yangtools</groupId>
         <artifactId>test-parent</artifactId>
-        <version>1.0</version>
+        <version>0.6.2-SNAPSHOT</version>
     </parent>
 
     <artifactId>invalid-version</artifactId>
@@ -48,7 +48,7 @@
             <plugin>
                 <groupId>org.opendaylight.yangtools</groupId>
                 <artifactId>yang-maven-plugin</artifactId>
-                <version>${it-project.version}</version>
+                <version>${project.version}</version>
                 <executions>
                     <execution>
                         <goals>
@@ -76,7 +76,7 @@
                     <dependency>
                         <groupId>org.opendaylight.yangtools</groupId>
                         <artifactId>yang-maven-plugin-spi</artifactId>
-                        <version>${it-project.version}</version>
+                        <version>${project.version}</version>
                         <type>test-jar</type>
                     </dependency>
                 </dependencies>
index 8ade2b1a9cff9883cc9df15831b40ee6f0bc0991..5641d3ac275f86cdfb7d947735e6117c89a4b078 100644 (file)
@@ -11,7 +11,7 @@
     <parent>
         <groupId>org.opendaylight.yangtools</groupId>
         <artifactId>test-parent</artifactId>
-        <version>1.0</version>
+        <version>0.6.2-SNAPSHOT</version>
     </parent>
 
     <artifactId>missing-yang-in-dep</artifactId>
@@ -21,7 +21,7 @@
             <plugin>
                 <groupId>org.opendaylight.yangtools</groupId>
                 <artifactId>yang-maven-plugin</artifactId>
-                <version>${it-project.version}</version>
+                <version>${project.version}</version>
                 <executions>
                     <execution>
                         <goals>
@@ -58,7 +58,7 @@
                     <dependency>
                         <groupId>org.opendaylight.yangtools</groupId>
                         <artifactId>yang-maven-plugin-spi</artifactId>
-                        <version>${it-project.version}</version>
+                        <version>${project.version}</version>
                         <type>test-jar</type>
                     </dependency>
                 </dependencies>
index 6fc2c8070530474f06cb82cd7cebec991ff63588..0fae6f793252995fb3fb30d911965f0d209ec469 100644 (file)
@@ -11,7 +11,7 @@
     <parent>
         <groupId>org.opendaylight.yangtools</groupId>
         <artifactId>test-parent</artifactId>
-        <version>1.0</version>
+        <version>0.6.2-SNAPSHOT</version>
     </parent>
 
     <artifactId>naming-conflict</artifactId>
@@ -21,7 +21,7 @@
             <plugin>
                 <groupId>org.opendaylight.yangtools</groupId>
                 <artifactId>yang-maven-plugin</artifactId>
-                <version>${it-project.version}</version>
+                <version>${project.version}</version>
                 <executions>
                     <execution>
                         <goals>
@@ -46,7 +46,7 @@
                     <dependency>
                         <groupId>org.opendaylight.yangtools</groupId>
                         <artifactId>maven-sal-api-gen-plugin</artifactId>
-                        <version>${it-project.version}</version>
+                        <version>${project.version}</version>
                         <type>jar</type>
                     </dependency>
                 </dependencies>
index d236de3e0afa46f4a10baec305422b5935ed5620..0a91e17da1c767c29b094e38fc51646598ee3667 100644 (file)
@@ -11,7 +11,7 @@
     <parent>
         <groupId>org.opendaylight.yangtools</groupId>
         <artifactId>test-parent</artifactId>
-        <version>1.0</version>
+        <version>0.6.2-SNAPSHOT</version>
     </parent>
 
     <artifactId>no-generators</artifactId>
             <plugin>
                 <groupId>org.opendaylight.yangtools</groupId>
                 <artifactId>yang-maven-plugin</artifactId>
-                <version>${it-project.version}</version>
+                <version>${project.version}</version>
                 <executions>
                     <execution>
                         <goals>
                             <goal>generate-sources</goal>
                         </goals>
-                        <configuration>
+                        <configuration combine.self="override">
                             <yangFilesRootDir>../files</yangFilesRootDir>
                             <inspectDependencies>false</inspectDependencies>
                             <codeGenerators>
@@ -49,7 +49,7 @@
                     <dependency>
                         <groupId>org.opendaylight.yangtools</groupId>
                         <artifactId>yang-maven-plugin-spi</artifactId>
-                        <version>${it-project.version}</version>
+                        <version>${project.version}</version>
                         <type>test-jar</type>
                     </dependency>
                 </dependencies>
index 20ba31ae929fe4a01dface59bbbca1836292c75c..0ead9af0c645f3291e2a252d0cc2f200866a1226 100644 (file)
@@ -11,7 +11,7 @@
     <parent>
         <groupId>org.opendaylight.yangtools</groupId>
         <artifactId>test-parent</artifactId>
-        <version>1.0</version>
+        <version>0.6.2-SNAPSHOT</version>
     </parent>
 
     <artifactId>no-output-dir</artifactId>
@@ -21,7 +21,7 @@
             <plugin>
                 <groupId>org.opendaylight.yangtools</groupId>
                 <artifactId>yang-maven-plugin</artifactId>
-                <version>${it-project.version}</version>
+                <version>${project.version}</version>
                 <executions>
                     <execution>
                         <goals>
@@ -48,7 +48,7 @@
                     <dependency>
                         <groupId>org.opendaylight.yangtools</groupId>
                         <artifactId>yang-maven-plugin-spi</artifactId>
-                        <version>${it-project.version}</version>
+                        <version>${project.version}</version>
                         <type>test-jar</type>
                     </dependency>
                 </dependencies>
index 3cac79593b097d8e8a4c3435e788fa7a0e9f7494..36603f33dbc71105c8f923fa4e42813f490edadf 100644 (file)
@@ -11,7 +11,7 @@
     <parent>
         <groupId>org.opendaylight.yangtools</groupId>
         <artifactId>test-parent</artifactId>
-        <version>1.0</version>
+        <version>0.6.2-SNAPSHOT</version>
     </parent>
 
     <artifactId>no-yang-files</artifactId>
@@ -21,7 +21,7 @@
             <plugin>
                 <groupId>org.opendaylight.yangtools</groupId>
                 <artifactId>yang-maven-plugin</artifactId>
-                <version>${it-project.version}</version>
+                <version>${project.version}</version>
                 <executions>
                     <execution>
                         <goals>
@@ -48,7 +48,7 @@
                     <dependency>
                         <groupId>org.opendaylight.yangtools</groupId>
                         <artifactId>yang-maven-plugin-spi</artifactId>
-                        <version>${it-project.version}</version>
+                        <version>${project.version}</version>
                         <type>test-jar</type>
                     </dependency>
                 </dependencies>
index 853e8e0c0e814af4ef001b87a3a83b0f2d72e711..b58576d5ea254f3e9fce2490250b10e1787fac7d 100644 (file)
@@ -11,7 +11,7 @@
     <parent>
         <groupId>org.opendaylight.yangtools</groupId>
         <artifactId>test-parent</artifactId>
-        <version>1.0</version>
+        <version>0.6.2-SNAPSHOT</version>
     </parent>
 
     <artifactId>unknown-generator</artifactId>
@@ -21,7 +21,7 @@
             <plugin>
                 <groupId>org.opendaylight.yangtools</groupId>
                 <artifactId>yang-maven-plugin</artifactId>
-                <version>${it-project.version}</version>
+                <version>${project.version}</version>
                 <executions>
                     <execution>
                         <goals>
@@ -56,7 +56,7 @@
                     <dependency>
                         <groupId>org.opendaylight.yangtools</groupId>
                         <artifactId>yang-maven-plugin-spi</artifactId>
-                        <version>${it-project.version}</version>
+                        <version>${project.version}</version>
                         <type>test-jar</type>
                     </dependency>
                 </dependencies>
index 8bb27a854b4154caed8c9d270c8ea8afa5e92e3e..1da608feb4dc7d0724a7aaf0a4d946d933d5daec 100644 (file)
@@ -11,7 +11,7 @@
     <parent>
         <groupId>org.opendaylight.yangtools</groupId>
         <artifactId>test-parent</artifactId>
-        <version>1.0</version>
+        <version>0.6.2-SNAPSHOT</version>
     </parent>
 
     <artifactId>yang-root-not-exist</artifactId>
@@ -21,7 +21,7 @@
             <plugin>
                 <groupId>org.opendaylight.yangtools</groupId>
                 <artifactId>yang-maven-plugin</artifactId>
-                <version>${it-project.version}</version>
+                <version>${project.version}</version>
                 <executions>
                     <execution>
                         <goals>
index 58085fce5f61564df11893f9529434f4c1d915fc..18aa62fd4f3899cb9ea1c4bb734758896d9f42be 100644 (file)
@@ -8,15 +8,17 @@
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
 
+    <parent>
+        <groupId>org.opendaylight.yangtools</groupId>
+        <artifactId>yangtools-parent</artifactId>
+        <version>0.6.2-SNAPSHOT</version>
+        <relativePath>/../../common/parent/pom.xml</relativePath>
+    </parent>
+
     <groupId>org.opendaylight.yangtools</groupId>
     <artifactId>test-parent</artifactId>
-    <version>1.0</version>
     <packaging>pom</packaging>
 
-    <properties>
-        <it-project.version>0.6.2-SNAPSHOT</it-project.version>
-    </properties>
-
     <modules>
         <module>additional-config</module>
         <module>correct</module>
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/AcceptingSchemaSourceFilter.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/AcceptingSchemaSourceFilter.java
deleted file mode 100644 (file)
index e101d9d..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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/eplv10.html
- */
-package org.opendaylight.yangtools.yang.model.repo.api;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableList.Builder;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-
-/**
- * A {@link SchemaSourceFilter} which accepts any schema source it is presented with.
- */
-public final class AcceptingSchemaSourceFilter implements SchemaSourceFilter {
-    private static final AcceptingSchemaSourceFilter INSTANCE = new AcceptingSchemaSourceFilter();
-
-    private final Iterable<Class<? extends SchemaSourceRepresentation>> representations;
-
-    private AcceptingSchemaSourceFilter() {
-        final Builder<Class<? extends SchemaSourceRepresentation>> b = ImmutableList.builder();
-        b.add(SchemaSourceRepresentation.class);
-        representations = b.build();
-    }
-
-    /**
-     * Return the singleton instance of this filter.
-     *
-     * @return Singleton shared instance.
-     */
-    public static final AcceptingSchemaSourceFilter getSingletonInstance() {
-        return INSTANCE;
-    }
-
-    @Override
-    public Iterable<Class<? extends SchemaSourceRepresentation>> supportedRepresentations() {
-        return representations;
-    }
-
-    @Override
-    public ListenableFuture<Boolean> apply(final SchemaSourceRepresentation schemaSource) {
-        return Futures.immediateFuture(Boolean.TRUE);
-    }
-}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/MissingSchemaSourceException.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/MissingSchemaSourceException.java
new file mode 100644 (file)
index 0000000..17c9c90
--- /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/eplv10.html
+ */
+package org.opendaylight.yangtools.yang.model.repo.api;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Exception thrown when a the specified schema source is not available.
+ */
+@Beta
+public class MissingSchemaSourceException extends SchemaSourceException {
+    private static final long serialVersionUID = 1L;
+
+    public MissingSchemaSourceException(final String message) {
+        super(message);
+    }
+
+    public MissingSchemaSourceException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+}
index 8869c9eb3ddb11a9d17732ba33bb0e0056e01986..2597e9c185481d8886d4ee65155e74d9264cdc37 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.yangtools.yang.model.repo.api;
 
+import com.google.common.annotations.Beta;
 import com.google.common.util.concurrent.CheckedFuture;
 
 import java.util.Collection;
@@ -20,6 +21,7 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext;
  * based on a specification of what {@link SourceIdentifier}s are required
  * and dynamic recursive resolution.
  */
+@Beta
 public interface SchemaContextFactory {
     /**
      * Create a new schema context containing specified sources, pulling in
index 807bbf05de62bcf4ae7d3863fa47a6e843e214d8..73569d384d3cd76091466ebe5474c70a96b57e6c 100644 (file)
@@ -7,6 +7,9 @@
  */
 package org.opendaylight.yangtools.yang.model.repo.api;
 
+import com.google.common.annotations.Beta;
+import com.google.common.util.concurrent.CheckedFuture;
+
 import javax.annotation.Nonnull;
 
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
@@ -15,6 +18,7 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext;
  * Interface exposed by repository implementations. A schema repository is a logically
  * centralized place for model storage and creation of {@link SchemaContext} instances.
  */
+@Beta
 public interface SchemaRepository {
     /**
      * Instantiate a new {@link SchemaContextFactory}, which will filter available schema
@@ -26,4 +30,6 @@ public interface SchemaRepository {
      * @return A new schema context factory.
      */
     SchemaContextFactory createSchemaContextFactory(@Nonnull SchemaSourceFilter filter);
+
+    <T extends SchemaSourceRepresentation> CheckedFuture<T, SchemaSourceException> getSchemaSource(@Nonnull SourceIdentifier id, @Nonnull Class<T> represetation);
 }
index b47e366a79755273b37be9e2a1a25335d3c6123a..e0be4adb7936c6afab7cd2be00e69440b6f4af0d 100644 (file)
@@ -7,38 +7,48 @@
  */
 package org.opendaylight.yangtools.yang.model.repo.api;
 
+import com.google.common.annotations.Beta;
 import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
 
-import java.util.Map;
+import java.util.Collection;
+import java.util.Collections;
 
 import javax.annotation.Nonnull;
 
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+
 /**
  * Exception thrown when a Schema Source fails to resolve.
  */
-public class SchemaResolutionException extends Exception {
+@Beta
+public class SchemaResolutionException extends SchemaSourceException {
     private static final long serialVersionUID = 1L;
-    private final Map<SourceIdentifier, Throwable> unresolvedSources;
+    private final Multimap<SourceIdentifier, ModuleImport> unsatisfiedImports;
+    private final Collection<SourceIdentifier> resolvedSources;
 
     public SchemaResolutionException(final @Nonnull String message) {
         this(message, (Throwable)null);
     }
 
     public SchemaResolutionException(final @Nonnull String message, final Throwable cause) {
-        this(message, cause, ImmutableMap.<SourceIdentifier, Exception>of());
+        this(message, cause, Collections.<SourceIdentifier>emptySet(), ImmutableMultimap.<SourceIdentifier, ModuleImport>of());
     }
 
-    public SchemaResolutionException(final @Nonnull String message, final @Nonnull Map<SourceIdentifier, ? extends Throwable> unresolvedSources) {
-        super(Preconditions.checkNotNull(message));
-        this.unresolvedSources = ImmutableMap.copyOf(unresolvedSources);
+    public SchemaResolutionException(final @Nonnull String message, final Collection<SourceIdentifier> resolvedSources,
+            final @Nonnull Multimap<SourceIdentifier, ModuleImport> unsatisfiedImports) {
+        this(message, null, Collections.<SourceIdentifier>emptySet(), unsatisfiedImports);
     }
 
-    public SchemaResolutionException(final @Nonnull String message, final Throwable cause, @Nonnull final Map<SourceIdentifier, ? extends Throwable> unresolvedSources) {
+    public SchemaResolutionException(final @Nonnull String message, final Throwable cause,
+            @Nonnull final Collection<SourceIdentifier> resolvedSources,
+            @Nonnull final Multimap<SourceIdentifier, ModuleImport> unsatisfiedImports) {
         super(message, cause);
-        this.unresolvedSources = ImmutableMap.copyOf(unresolvedSources);
+        this.unsatisfiedImports = ImmutableMultimap.copyOf(unsatisfiedImports);
+        this.resolvedSources = ImmutableList.copyOf(resolvedSources);
     }
 
     /**
@@ -47,13 +57,19 @@ public class SchemaResolutionException extends Exception {
      *
      * @return Source/reason map.
      */
-    public final Map<SourceIdentifier, Throwable> getUnresolvedSources() {
-        return unresolvedSources;
+    public final Multimap<SourceIdentifier, ModuleImport> getUnsatisfiedImports() {
+        return unsatisfiedImports;
+    }
+
+
+    // FIXME: should be leak actual mapping?
+    public final Collection<SourceIdentifier> getResolvedSources() {
+        return resolvedSources;
     }
 
     @Override
     public final String toString() {
-        return addToStringAttributes(Objects.toStringHelper(this).add("unresolvedSources", unresolvedSources)).toString();
+        return addToStringAttributes(Objects.toStringHelper(this).add("unsatisfiedImports", unsatisfiedImports)).toString();
     }
 
     protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
@@ -5,20 +5,22 @@
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
  * and is available at http://www.eclipse.org/legal/eplv10.html
  */
-package org.opendaylight.yangtools.yang.model.repo.spi;
+package org.opendaylight.yangtools.yang.model.repo.api;
+
+import com.google.common.annotations.Beta;
 
 /**
- * Exception thrown when a failure to translate a schema source between
- * representations.
+ * Exception thrown when a failure to acquire a schema source occurs.
  */
-public class SchemaSourceTransformationException extends Exception {
+@Beta
+public class SchemaSourceException extends Exception {
     private static final long serialVersionUID = 1L;
 
-    public SchemaSourceTransformationException(final String message) {
+    public SchemaSourceException(final String message) {
         super(message);
     }
 
-    public SchemaSourceTransformationException(final String message, final Throwable cause) {
+    public SchemaSourceException(final String message, final Throwable cause) {
         super(message, cause);
     }
 }
index 188f83c0c592c47b6134bf080d4ed0a99924dfce..f881900754a53642d3696bfab3e67e2a47934ac2 100644 (file)
@@ -7,9 +7,55 @@
  */
 package org.opendaylight.yangtools.yang.model.repo.api;
 
+import com.google.common.annotations.Beta;
+import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 
+import java.util.Collections;
+
+/*
+ * A filter of schema sources. This is used to restrict which sources representation
+ * instances are allowed to participate in construction of a schema context. This
+ * allows, for example, to create an non-shared island, or require the sources to
+ * be certified before use.
+ */
+@Beta
 public interface SchemaSourceFilter {
+    /**
+     * A {@link SchemaSourceFilter} which accepts any schema source it is presented with.
+     */
+    public static final SchemaSourceFilter ALWAYS_ACCEPT = new SchemaSourceFilter() {
+        private final Iterable<Class<? extends SchemaSourceRepresentation>> REPRESENTATIONS =
+                Collections.<Class<? extends SchemaSourceRepresentation>>singletonList(SchemaSourceRepresentation.class);
+
+        @Override
+        public Iterable<Class<? extends SchemaSourceRepresentation>> supportedRepresentations() {
+            return REPRESENTATIONS;
+        }
+
+        @Override
+        public ListenableFuture<Boolean> apply(final SchemaSourceRepresentation schemaSource) {
+            return Futures.immediateFuture(Boolean.TRUE);
+        }
+    };
+
+    /**
+     * Get the representations this filter supports. A schema source is translated
+     * into one of these representations before it is presented for filtering.
+     *
+     * @return Set of supported representations.
+     */
     Iterable<Class<? extends SchemaSourceRepresentation>> supportedRepresentations();
+
+    /**
+     * Check if a particular schema source is acceptable to the filter. The process
+     * of checking may be asynchronous, but at some point it needs to produce an
+     * affirmative or negative answer before the schema context construction can
+     * proceed.
+     *
+     * @param schemaSource Schema source to be filtered
+     * @return Promise of a filtering decision. The result should be {@link Boolean#TRUE}
+     *         if the source is acceptable.
+     */
     ListenableFuture<Boolean> apply(SchemaSourceRepresentation schemaSource);
 }
index b1261a9cd48a5883c86b955b12c3f187315a5cf8..90fc5ae740c2bf5cf2d07f0328681d1e1f987f07 100644 (file)
@@ -7,6 +7,8 @@
  */
 package org.opendaylight.yangtools.yang.model.repo.api;
 
+import com.google.common.annotations.Beta;
+
 import org.opendaylight.yangtools.concepts.Identifiable;
 import org.opendaylight.yangtools.concepts.Immutable;
 
@@ -35,6 +37,7 @@ import org.opendaylight.yangtools.concepts.Immutable;
  * Implementations of this interface expected to comply with the {@link Immutable}
  * contract.
  */
+@Beta
 public interface SchemaSourceRepresentation extends Identifiable<SourceIdentifier>, Immutable {
     /**
      * {@inheritDoc}
index cd3a0fbefcaaed7198980ee545f7fe510654ac0b..58b011c36811cd60c3f9c0af9a1a7f93d9990988 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.yangtools.yang.model.repo.api;
 
+import com.google.common.annotations.Beta;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 
@@ -14,7 +15,6 @@ import org.opendaylight.yangtools.concepts.Identifier;
 import org.opendaylight.yangtools.concepts.Immutable;
 
 /**
- *
  * YANG Schema source identifier
  *
  * Simple transfer object represents identifier of source for YANG schema (module or submodule),
@@ -34,9 +34,8 @@ import org.opendaylight.yangtools.concepts.Immutable;
  * <p>
  * (For further reference see: http://tools.ietf.org/html/rfc6020#section-5.2 and
  * http://tools.ietf.org/html/rfc6022#section-3.1 ).
- *
- *
  */
+@Beta
 public final class SourceIdentifier implements Identifier, Immutable {
     private static final long serialVersionUID = 1L;
     private final String revision;
index be52a90f777131055f9423d98d9893d0568c3371..50ee12ef4d03358729456081c76216a020f5db71 100644 (file)
@@ -7,8 +7,12 @@
  */
 package org.opendaylight.yangtools.yang.model.repo.api;
 
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.annotations.Beta;
 import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.io.ByteSource;
 
@@ -21,6 +25,7 @@ import org.opendaylight.yangtools.concepts.Delegator;
  * YANG text schema source representation. Exposes an RFC6020 text representation
  * as an {@link InputStream}.
  */
+@Beta
 public abstract class YangTextSchemaSource extends ByteSource implements SchemaSourceRepresentation {
     private final SourceIdentifier identifier;
 
@@ -28,6 +33,12 @@ public abstract class YangTextSchemaSource extends ByteSource implements SchemaS
         this.identifier = Preconditions.checkNotNull(identifier);
     }
 
+    public static SourceIdentifier identifierFromFilename(final String name) {
+        checkArgument(name.endsWith(".yang"), "Filename %s does not have a .yang extension", name);
+        // FIXME: add revision-awareness
+        return SourceIdentifier.create(name.substring(0, name.length() - 5), Optional.<String>absent());
+    }
+
     /**
      * {@inheritDoc}
      */
index b67aae628914c20e7b5fe2779152abb63814c2a1..f09f525533e62982f6610c96344501414bcfa8b4 100644 (file)
@@ -7,12 +7,15 @@
  */
 package org.opendaylight.yangtools.yang.model.repo.api;
 
+import com.google.common.annotations.Beta;
+
 import org.w3c.dom.Document;
 
 /**
  * Yin schema source representation. Exposes an RFC6020 YIN XML representation
  * as an W3C {@link Document}.
  */
+@Beta
 public interface YinSchemaSource extends SchemaSourceRepresentation {
     /**
      * {@inheritDoc}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/PotentialSchemaSource.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/PotentialSchemaSource.java
new file mode 100644 (file)
index 0000000..fae4467
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * 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/eplv10.html
+ */
+package org.opendaylight.yangtools.yang.model.repo.spi;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+
+/**
+ * A potential schema source. Instances of this class track the various
+ * representations of a schema source and the cost attached to obtaining
+ * the source from them.
+ */
+@Beta
+public final class PotentialSchemaSource<T extends SchemaSourceRepresentation> {
+    /**
+     * Each registered source has a cost associated with it. Since a particular
+     * representation can be acquired by various means, here are general constants
+     * for common cases.
+     */
+    public enum Costs {
+        /**
+         * The source is immediately available, via a lookup or similar.
+         */
+        IMMEDIATE(0),
+        /**
+         * The source is available via a computation. For transformation-type
+         * computation, the cost of acquiring the cost needs to be added, too.
+         */
+        COMPUTATION(1),
+        /**
+         * The source is available by performing local IO, such that reading
+         * from a disk.
+         */
+        LOCAL_IO(4),
+        /**
+         * The source is available by performing remote IO, such as fetching
+         * from an HTTP server or similar.
+         */
+        REMOTE_IO(8);
+
+        private final int value;
+
+        private Costs(final int value) {
+            this.value = value;
+        }
+
+        /**
+         * The the cost value.
+         *
+         * @return Const constant.
+         */
+        public int getValue() {
+            return value;
+        }
+    }
+
+    private final Class<? extends T> representation;
+    private final SourceIdentifier sourceIdentifier;
+    private final int cost;
+
+    private PotentialSchemaSource(final SourceIdentifier sourceIdentifier, final Class<? extends T> representation, final int cost) {
+        this.representation = Preconditions.checkNotNull(representation);
+        this.sourceIdentifier = Preconditions.checkNotNull(sourceIdentifier);
+        Preconditions.checkArgument(cost >= 0, "cost has to be non-negative");
+        this.cost = cost;
+    }
+
+    public static final <T extends SchemaSourceRepresentation> PotentialSchemaSource<T> create(final SourceIdentifier sourceIdentifier, final Class<? extends T> representation, final int cost) {
+        return new PotentialSchemaSource<>(sourceIdentifier, representation, cost);
+    }
+
+    public SourceIdentifier getSourceIdentifier() {
+        return sourceIdentifier;
+    }
+
+    public Class<? extends T> getRepresentation() {
+        return representation;
+    }
+
+    public int getCost() {
+        return cost;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + cost;
+        result = prime * result + representation.hashCode();
+        result = prime * result + sourceIdentifier.hashCode();
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof PotentialSchemaSource)) {
+            return false;
+        }
+        final PotentialSchemaSource<?> other = (PotentialSchemaSource<?>) obj;
+        if (cost != other.cost) {
+            return false;
+        }
+        if (!representation.equals(other.representation)) {
+            return false;
+        }
+        if (!sourceIdentifier.equals(other.sourceIdentifier)) {
+            return false;
+        }
+        return true;
+    }
+}
@@ -7,9 +7,14 @@
  */
 package org.opendaylight.yangtools.yang.model.repo.spi;
 
-import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import com.google.common.annotations.Beta;
+
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+
+/**
+ * Registration of a SchemaSourceListener.
+ */
+@Beta
+public interface SchemaListenerRegistration extends ListenerRegistration<SchemaSourceListener> {
 
-public interface SchemaTransformerRegistration extends ObjectRegistration<SchemaSourceTransformer<?, ?>> {
-    @Override
-    void close();
 }
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceListener.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceListener.java
new file mode 100644 (file)
index 0000000..b4f55a8
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * 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/eplv10.html
+ */
+package org.opendaylight.yangtools.yang.model.repo.spi;
+
+import com.google.common.annotations.Beta;
+
+import java.util.EventListener;
+
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+
+/**
+ * Listener for schema source lifecycle events.
+ */
+@Beta
+public interface SchemaSourceListener extends EventListener {
+    /**
+     * Invoked when the registry sees a concrete source. This callback is typically
+     * used by cache-type listeners, who intercept the source, store it locally and
+     * announce themselves as a provider of that particular schema source.
+     *
+     * @param source Schema source
+     */
+    void schemaSourceEncountered(SchemaSourceRepresentation source);
+
+    /**
+     * Invoked when a new schema source is registered by a provider. This call
+     * callback, along with {@link #schemaSourceUnregistered(PotentialSchemaSource)}
+     * is typically used by transformer-type listeners, who intercept the registration
+     * if the advertised representation matches their input type and register
+     * themselves as a potential provider of the same source in their output
+     * representation type.
+     *
+     * @param sources Newly available sources
+     */
+    void schemaSourceRegistered(Iterable<PotentialSchemaSource<?>> sources);
+
+    /**
+     * Invoked when a schema source is unregistered.
+     *
+     * @param source Schema source representation
+     */
+    void schemaSourceUnregistered(PotentialSchemaSource<?> source);
+}
index a0a141bd3759f4f1a4dac00e3f3cb2706d1114bd..70ad6813e8286f4c22025b312395af34ebbc6418 100644 (file)
@@ -8,9 +8,10 @@
 package org.opendaylight.yangtools.yang.model.repo.spi;
 
 import com.google.common.annotations.Beta;
-import com.google.common.base.Optional;
-import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.CheckedFuture;
 
+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;
 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
 
@@ -29,10 +30,9 @@ public interface SchemaSourceProvider<T extends SchemaSourceRepresentation> {
      *
      * <ul>
      * <li> If the source identifier specifies a revision, this method returns either
-     * a representation of that particular revision, or report the identifier as absent
-     * by returning {@link Optional#absent()}.
+     * a representation of that particular revision or throw {@link MissingSchemaSourceException}.
      * <li> If the source identifier does not specify a revision, this method returns
-     * the newest available revision, or {@link Optional#absent()}.
+     * the newest available revision, or throws {@link MissingSchemaSourceException}.
      *
      * In either case the returned representation is required to report a non-null
      * revision in the {@link SourceIdentifier} returned from
@@ -43,7 +43,7 @@ public interface SchemaSourceProvider<T extends SchemaSourceRepresentation> {
      *
      * @param sourceIdentifier source identifier
      * @return source representation if supplied YANG module is available
-     *         {@link Optional#absent()} otherwise.
+     *
      */
-    ListenableFuture<Optional<T>> getSource(SourceIdentifier sourceIdentifier);
+    CheckedFuture<? extends T, SchemaSourceException> getSource(SourceIdentifier sourceIdentifier);
 }
index 3c3c5fef7748dda7a9bdbe3ae507118a46c4f617..6937c53e98ecbc42544cf61fa7f00b0ee792e880 100644 (file)
@@ -7,10 +7,16 @@
  */
 package org.opendaylight.yangtools.yang.model.repo.spi;
 
+import com.google.common.annotations.Beta;
+
 import org.opendaylight.yangtools.concepts.ObjectRegistration;
-import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
 
-public interface SchemaSourceRegistration extends ObjectRegistration<SourceIdentifier> {
+/**
+ * Registration of a schema source.
+ */
+@Beta
+public interface SchemaSourceRegistration<T extends SchemaSourceRepresentation> extends ObjectRegistration<PotentialSchemaSource<T>> {
     @Override
     void close();
 }
index f9acf3d456cb260f4f924356c08caad549e4b128..075fcb5382d44ffb34532d20685c92de4cf86c7a 100644 (file)
@@ -7,6 +7,8 @@
  */
 package org.opendaylight.yangtools.yang.model.repo.spi;
 
+import com.google.common.annotations.Beta;
+
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
 
@@ -17,29 +19,28 @@ import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
  * {@link SchemaSourceProvider} instances which would then acquire the schema
  * source.
  */
+@Beta
 public interface SchemaSourceRegistry {
     /**
      * Register a new schema source which is potentially available from a provider.
      * A registration does not guarantee that a subsequent call to
      * {@link SchemaSourceProvider#getSource(SourceIdentifier)} will succeed.
      *
-     * @param identifier Schema source identifier
      * @param provider Resolver which can potentially resolve the identifier
-     * @param representation Schema source representation which the source may
-     *                       be available.
+     * @param source Schema source details
      * @return A registration handle. Invoking {@link SchemaSourceRegistration#close()}
      *         will cancel the registration.
      */
-    <T extends SchemaSourceRepresentation> SchemaSourceRegistration registerSchemaSource(
-            SourceIdentifier identifier, SchemaSourceProvider<? super T> provider, Class<T> representation);
+    <T extends SchemaSourceRepresentation> SchemaSourceRegistration<T> registerSchemaSource(SchemaSourceProvider<? super T> provider, PotentialSchemaSource<T> source);
 
     /**
-     * Register a schema transformer. The registry can invoke it to transform between
-     * the various schema source formats.
+     * Register a schema source listener. The listener will be notified as new
+     * sources and their representations become available, subject to the provided
+     * filter.
      *
-     * @param transformer Schema source transformer
-     * @return A registration handle. Invoking {@link SchemaTransformerRegistration#close()}
+     * @param listener Schema source listener
+     * @return A registration handle. Invoking {@link SchemaListenerRegistration#close()}
      *         will cancel the registration.
      */
-    SchemaTransformerRegistration registerSchemaSourceTransformer(SchemaSourceTransformer<?, ?> transformer);
+    SchemaListenerRegistration registerSchemaSourceListener(SchemaSourceListener listener);
 }
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceTransformer.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceTransformer.java
deleted file mode 100644 (file)
index 13d309e..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * 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/eplv10.html
- */
-package org.opendaylight.yangtools.yang.model.repo.spi;
-
-import com.google.common.util.concurrent.CheckedFuture;
-
-import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
-
-/**
- * An schema source representation transformation service. An instance can create
- * some output schema source representation based on some input source representation.
- *
- * @param <I> Input {@link SchemaSourceRepresentation}
- * @param <O> Output {@link SchemaSourceRepresentation}
- */
-public interface SchemaSourceTransformer<I extends SchemaSourceRepresentation, O extends SchemaSourceRepresentation> {
-    /**
-     * Return the {@link SchemaSourceRepresentation} which this transformer
-     * accepts on its input.
-     *
-     * @return The input source representation type.
-     */
-    Class<I> getInputRepresentation();
-
-    /**
-     * Return the {@link SchemeSourceRepresentation} which this transformer
-     * produces on its output.
-     *
-     * @return The output source representation type.
-     */
-    Class<O> getOutputRepresentation();
-
-    /**
-     * Transform a schema source representation from its input form to
-     * the transformers output form.
-     *
-     * @param source Schema source in its source representation
-     * @return A future which produces the output schema source representation.
-     */
-    CheckedFuture<O, SchemaSourceTransformationException> transformSchemaSource(I source);
-
-    /**
-     * Return the relative cost of performing the transformation. When in doubt,
-     * return 1.
-     *
-     * @return Relative cost.
-     */
-    int getCost();
-}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaListenerRegistration.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaListenerRegistration.java
new file mode 100644 (file)
index 0000000..e720f8d
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * 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/eplv10.html
+ */
+package org.opendaylight.yangtools.yang.model.repo.util;
+
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaListenerRegistration;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceListener;
+
+public abstract class AbstractSchemaListenerRegistration extends AbstractListenerRegistration<SchemaSourceListener> implements SchemaListenerRegistration {
+    protected AbstractSchemaListenerRegistration(final SchemaSourceListener listener) {
+        super(listener);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaRepository.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaRepository.java
new file mode 100644 (file)
index 0000000..53563fd
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * 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/eplv10.html
+ */
+package org.opendaylight.yangtools.yang.model.repo.util;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureFallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.annotation.concurrent.GuardedBy;
+
+import org.opendaylight.yangtools.util.concurrent.ExceptionMapper;
+import org.opendaylight.yangtools.util.concurrent.ReflectiveExceptionMapper;
+import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaListenerRegistration;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceListener;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Abstract base class for {@link SchemaRepository} implementations. It handles registration
+ * and lookup of schema sources, subclasses need only to provide their own
+ * {@link #createSchemaContextFactory(SchemaSourceFilter)} implementation.
+ */
+@Beta
+public abstract class AbstractSchemaRepository implements SchemaRepository, SchemaSourceRegistry {
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractSchemaRepository.class);
+    private static final ExceptionMapper<SchemaSourceException> FETCH_MAPPER = ReflectiveExceptionMapper.create("Schema source fetch", SchemaSourceException.class);
+
+    /*
+     * Source identifier -> representation -> provider map. We usually are looking for
+     * a specific representation of a source.
+     */
+    @GuardedBy("this")
+    private final Map<SourceIdentifier, Multimap<Class<? extends SchemaSourceRepresentation>, AbstractSchemaSourceRegistration<?>>> sources = new HashMap<>();
+
+    /*
+     * Schema source listeners.
+     */
+    @GuardedBy("this")
+    private final Collection<SchemaListenerRegistration> listeners = new ArrayList<>();
+
+    private static final <T extends SchemaSourceRepresentation> CheckedFuture<T, SchemaSourceException> fetchSource(final SourceIdentifier id, final Iterator<AbstractSchemaSourceRegistration<?>> it) {
+        final AbstractSchemaSourceRegistration<?> reg = it.next();
+
+        @SuppressWarnings("unchecked")
+        final CheckedFuture<? extends T, SchemaSourceException> f = ((SchemaSourceProvider<T>)reg.getProvider()).getSource(id);
+        return Futures.makeChecked(Futures.withFallback(f, new FutureFallback<T>() {
+            @Override
+            public ListenableFuture<T> create(final Throwable t) throws SchemaSourceException {
+                LOG.debug("Failed to acquire source from {}", reg, t);
+
+                if (it.hasNext()) {
+                    return fetchSource(id, it);
+                }
+
+                throw new MissingSchemaSourceException("All available providers exhausted");
+            }
+        }), FETCH_MAPPER);
+    }
+
+    @Override
+    public <T extends SchemaSourceRepresentation> CheckedFuture<T, SchemaSourceException> getSchemaSource(final SourceIdentifier id, final Class<T> representation) {
+        final Multimap<Class<? extends SchemaSourceRepresentation>, AbstractSchemaSourceRegistration<?>> srcs = sources.get(id);
+        if (srcs == null) {
+            return Futures.<T, SchemaSourceException>immediateFailedCheckedFuture(new MissingSchemaSourceException("No providers registered for source" + id));
+        }
+
+        final Iterator<AbstractSchemaSourceRegistration<?>> regs = srcs.get(representation).iterator();
+        if (!regs.hasNext()) {
+            return Futures.<T, SchemaSourceException>immediateFailedCheckedFuture(
+                    new MissingSchemaSourceException("No providers for source " + id + " representation " + representation + " available"));
+        }
+
+        return fetchSource(id, regs);
+    }
+
+    private synchronized <T extends SchemaSourceRepresentation> void addSource(final PotentialSchemaSource<T> source, final AbstractSchemaSourceRegistration<T> reg) {
+        Multimap<Class<? extends SchemaSourceRepresentation>, AbstractSchemaSourceRegistration<?>> m = sources.get(source.getSourceIdentifier());
+        if (m == null) {
+            m = HashMultimap.create();
+            sources.put(source.getSourceIdentifier(), m);
+        }
+
+        m.put(source.getRepresentation(), reg);
+
+        final Collection<PotentialSchemaSource<?>> reps = Collections.<PotentialSchemaSource<?>>singleton(source);
+        for (SchemaListenerRegistration l : listeners) {
+            l.getInstance().schemaSourceRegistered(reps);
+        }
+    }
+
+    private synchronized <T extends SchemaSourceRepresentation> void removeSource(final PotentialSchemaSource<?> source, final SchemaSourceRegistration<?> reg) {
+        final Multimap<Class<? extends SchemaSourceRepresentation>, AbstractSchemaSourceRegistration<?>> m = sources.get(source.getSourceIdentifier());
+        if (m != null) {
+            m.remove(source.getRepresentation(), reg);
+
+            for (SchemaListenerRegistration l : listeners) {
+                l.getInstance().schemaSourceUnregistered(source);
+            }
+
+            if (m.isEmpty()) {
+                sources.remove(m);
+            }
+        }
+    }
+
+    @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) {
+            @Override
+            protected void removeRegistration() {
+                removeSource(source, this);
+            }
+        };
+
+        addSource(source, ret);
+        return ret;
+    }
+
+    @Override
+    public SchemaListenerRegistration registerSchemaSourceListener(final SchemaSourceListener listener) {
+        final SchemaListenerRegistration ret = new AbstractSchemaListenerRegistration(listener) {
+            @Override
+            protected void removeRegistration() {
+                listeners.remove(this);
+            }
+        };
+
+        synchronized (this) {
+            final Collection<PotentialSchemaSource<?>> col = new ArrayList<>();
+            for (Multimap<Class<? extends SchemaSourceRepresentation>, AbstractSchemaSourceRegistration<?>> m : sources.values()) {
+                for (AbstractSchemaSourceRegistration<?> r : m.values()) {
+                    col.add(r.getInstance());
+                }
+            }
+
+            // Notify first, so translator-type listeners, who react by registering a source
+            // do not cause infinite loop.
+            listener.schemaSourceRegistered(col);
+            listeners.add(ret);
+        }
+        return ret;
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaSourceCache.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaSourceCache.java
new file mode 100644 (file)
index 0000000..498f403
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * 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.model.repo.util;
+
+import com.google.common.base.Preconditions;
+
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource.Costs;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceListener;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
+
+/**
+ * Abstract base class for cache-type SchemaSourceListeners. It needs to be
+ * registered with a {@link SchemaSourceRegistry}, where it gets notifications
+ * from. It performs filtering and {@link #offer(SchemaSourceRepresentation)}s
+ * conforming sources to the subclass.
+ *
+ * @param <T> Cached schema source type.
+ */
+public abstract class AbstractSchemaSourceCache<T extends SchemaSourceRepresentation> implements SchemaSourceListener, SchemaSourceProvider<T> {
+    private final SchemaSourceRegistry consumer;
+    private final Class<T> representation;
+    private final Costs cost;
+
+    protected AbstractSchemaSourceCache(final SchemaSourceRegistry consumer, final Class<T> representation, final Costs cost) {
+        this.consumer = Preconditions.checkNotNull(consumer);
+        this.representation = Preconditions.checkNotNull(representation);
+        this.cost = Preconditions.checkNotNull(cost);
+    }
+
+    /**
+     * Offer a schema source in requested representation for caching. Subclasses
+     * need to implement this method to store the schema source. Once they have
+     * determined to cache the source, they should call {@link #register(SourceIdentifier)}.
+     *
+     * @param source schema source
+     */
+    protected abstract void offer(T source);
+
+    /**
+     * Register the presence of a cached schema source with the consumer. Subclasses
+     * need to call this method once they have cached a schema source representation,
+     * or when they have determined they have a schema source is available -- like
+     * when a persistent cache reads its cache index.
+     *
+     * @param sourceIdentifier Source identifier
+     * @return schema source registration, which the subclass needs to
+     *         {@link SchemaSourceRegistration#close() once it expunges the source
+     *         from the cache.
+     */
+    protected final SchemaSourceRegistration<T> register(final SourceIdentifier sourceIdentifier) {
+        final PotentialSchemaSource<T> src = PotentialSchemaSource.create(sourceIdentifier, representation, cost.getValue());
+        return consumer.registerSchemaSource(this, src);
+    }
+
+    @Override
+    public void schemaSourceEncountered(final SchemaSourceRepresentation source) {
+        if (representation.isAssignableFrom(source.getType())) {
+            @SuppressWarnings("unchecked")
+            final T src = (T)source;
+            offer(src);
+        }
+    }
+
+    @Override
+    public final void schemaSourceRegistered(final Iterable<PotentialSchemaSource<?>> sources) {
+        // Not interesting
+    }
+
+    @Override
+    public final void schemaSourceUnregistered(final PotentialSchemaSource<?> source) {
+        // Not interesting
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaSourceRegistration.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaSourceRegistration.java
new file mode 100644 (file)
index 0000000..241cc15
--- /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/eplv10.html
+ */
+package org.opendaylight.yangtools.yang.model.repo.util;
+
+import com.google.common.base.Preconditions;
+
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
+
+public abstract class AbstractSchemaSourceRegistration<T extends SchemaSourceRepresentation> extends AbstractObjectRegistration<PotentialSchemaSource<T>> implements SchemaSourceRegistration<T> {
+    private final SchemaSourceProvider<?> provider;
+
+    protected AbstractSchemaSourceRegistration(final SchemaSourceProvider<?> provider, final PotentialSchemaSource<T> source) {
+        super(source);
+        this.provider = Preconditions.checkNotNull(provider);
+    }
+
+    protected final SchemaSourceProvider<?> getProvider() {
+        return provider;
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/InMemorySchemaSourceCache.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/InMemorySchemaSourceCache.java
new file mode 100644 (file)
index 0000000..ac820d2
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * 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.model.repo.util;
+
+import com.google.common.base.Preconditions;
+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 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;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource.Costs;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
+
+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;
+
+    protected InMemorySchemaSourceCache(final SchemaSourceRegistry consumer, final Class<T> representation, final int maxSize) {
+        super(consumer, representation, Costs.IMMEDIATE);
+        cache = CacheBuilder.newBuilder().softValues().maximumSize(maxSize).removalListener(LISTENER).build();
+    }
+
+    @Override
+    public CheckedFuture<? extends T, SchemaSourceException> getSource(final SourceIdentifier sourceIdentifier) {
+        final CacheEntry<T> present = cache.getIfPresent(sourceIdentifier);
+        if (present != null) {
+            return Futures.immediateCheckedFuture(present.source);
+        }
+
+        return Futures.<T, SchemaSourceException>immediateFailedCheckedFuture(new MissingSchemaSourceException("Source not found"));
+    }
+
+    @Override
+    protected void offer(final T source) {
+        final CacheEntry<T> present = cache.getIfPresent(source.getIdentifier());
+        if (present == null) {
+            final SchemaSourceRegistration<T> reg = register(source.getIdentifier());
+            cache.put(source.getIdentifier(), new CacheEntry<T>(source, reg));
+        }
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/RefcountedRegistration.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/RefcountedRegistration.java
new file mode 100644 (file)
index 0000000..3cd59b2
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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.model.repo.util;
+
+import com.google.common.base.Preconditions;
+
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
+
+final class RefcountedRegistration {
+    private final SchemaSourceRegistration<?> reg;
+    private int refcount = 1;
+
+    RefcountedRegistration(final SchemaSourceRegistration<?> reg) {
+        this.reg = Preconditions.checkNotNull(reg);
+    }
+
+    public void incRef() {
+        refcount++;
+    }
+
+    public boolean decRef() {
+        Preconditions.checkState(refcount > 0, "Refcount underflow: %s", refcount);
+
+        if (0 == --refcount) {
+            reg.close();
+            return true;
+        } else {
+            return false;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/SchemaSourceTransformer.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/SchemaSourceTransformer.java
new file mode 100644 (file)
index 0000000..7b58006
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * 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.model.repo.util;
+
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.opendaylight.yangtools.util.concurrent.ExceptionMapper;
+import org.opendaylight.yangtools.util.concurrent.ReflectiveExceptionMapper;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceListener;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
+
+public class SchemaSourceTransformer<S extends SchemaSourceRepresentation, D extends SchemaSourceRepresentation> implements SchemaSourceListener, SchemaSourceProvider<D> {
+    private static final ExceptionMapper<SchemaSourceException> MAPPER = ReflectiveExceptionMapper.create("Source transformation", SchemaSourceException.class);
+
+    public static interface Transformation<S extends SchemaSourceRepresentation, D extends SchemaSourceRepresentation> extends AsyncFunction<S, D> {
+        @Override
+        CheckedFuture<D, SchemaSourceException> apply(final S input) throws Exception;
+    }
+
+    private final Map<PotentialSchemaSource<?>, RefcountedRegistration> sources = new HashMap<>();
+    private final SchemaSourceRegistry consumer;
+    private final SchemaRepository provider;
+    private final AsyncFunction<S, D> function;
+    private final Class<S> srcClass;
+    private final Class<D> dstClass;
+
+    public SchemaSourceTransformer(final SchemaRepository provider, final Class<S> srcClass,
+            final SchemaSourceRegistry consumer, final Class<D> dstClass, final AsyncFunction<S, D> function) {
+        this.provider = Preconditions.checkNotNull(provider);
+        this.consumer = Preconditions.checkNotNull(consumer);
+        this.function = Preconditions.checkNotNull(function);
+        this.srcClass = Preconditions.checkNotNull(srcClass);
+        this.dstClass = Preconditions.checkNotNull(dstClass);
+    }
+
+    @Override
+    public CheckedFuture<D, SchemaSourceException> getSource(final SourceIdentifier sourceIdentifier) {
+        final CheckedFuture<S, SchemaSourceException> f = provider.getSchemaSource(sourceIdentifier, srcClass);
+        return Futures.makeChecked(Futures.transform(f, function), MAPPER);
+    }
+
+    @Override
+    public final void schemaSourceEncountered(final SchemaSourceRepresentation source) {
+        // Not interesting
+    }
+
+    @Override
+    public final void schemaSourceRegistered(final Iterable<PotentialSchemaSource<?>> sources) {
+        for (PotentialSchemaSource<?> src : sources) {
+            final Class<?> rep = src.getRepresentation();
+            if (srcClass.isAssignableFrom(rep) && dstClass != rep) {
+                registerSource(src);
+            }
+        }
+    }
+
+    @Override
+    public final void schemaSourceUnregistered(final PotentialSchemaSource<?> source) {
+        final Class<?> rep = source.getRepresentation();
+        if (srcClass.isAssignableFrom(rep) && dstClass != rep) {
+            unregisterSource(source);
+        }
+    }
+
+    private void registerSource(final PotentialSchemaSource<?> src) {
+        RefcountedRegistration reg = sources.get(src);
+        if (reg != null) {
+            reg.incRef();
+            return;
+        }
+
+        final PotentialSchemaSource<D> newSrc = PotentialSchemaSource.create(src.getSourceIdentifier(), dstClass,
+                src.getCost() + PotentialSchemaSource.Costs.COMPUTATION.getValue());
+
+        final SchemaSourceRegistration<D> r = consumer.registerSchemaSource(this, newSrc);
+        sources.put(src, new RefcountedRegistration(r));
+    }
+
+    private void unregisterSource(final PotentialSchemaSource<?> src) {
+        final RefcountedRegistration reg = sources.get(src);
+        if (reg != null && reg.decRef()) {
+            sources.remove(src);
+        }
+    }
+}
index 002746fa571d8b9616e4ca78ddfd406bcfd005ef..0e40bd2964472c4a354f3d99e61b5c9893043787 100644 (file)
@@ -191,7 +191,7 @@ public final class BaseTypes {
         } else if ("empty".equals(typeName)) {
             return EmptyType.getInstance();
         } else if ("instance-identifier".equals(typeName)) {
-            return InstanceIdentifier.getInstance();
+            return InstanceIdentifierType.getInstance();
         }
         return null;
     }
similarity index 87%
rename from yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/InstanceIdentifier.java
rename to yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/InstanceIdentifierType.java
index 138b6a50a9b3e045f39b3c2c9a22e3e637a62d8a..02289efb0531f8b25931c5f513cec7379738baa4 100644 (file)
@@ -29,7 +29,7 @@ import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefi
  * @see InstanceIdentifierTypeDefinition
  *
  */
-public final class InstanceIdentifier implements InstanceIdentifierTypeDefinition, Immutable {
+public final class InstanceIdentifierType implements InstanceIdentifierTypeDefinition, Immutable {
 
     private static final QName NAME = BaseTypes.INSTANCE_IDENTIFIER_QNAME;
     private static final SchemaPath PATH = SchemaPath.create(true, NAME);
@@ -40,8 +40,8 @@ public final class InstanceIdentifier implements InstanceIdentifierTypeDefinitio
     private static final String UNITS = "";
     private final Boolean requireInstance;
 
-    private static final InstanceIdentifier INSTANCE_WITH_REQUIRED_TRUE = new InstanceIdentifier(true);
-    private static final InstanceIdentifier INSTANCE_WITH_REQUIRED_FALSE = new InstanceIdentifier(false);
+    private static final InstanceIdentifierType INSTANCE_WITH_REQUIRED_TRUE = new InstanceIdentifierType(true);
+    private static final InstanceIdentifierType INSTANCE_WITH_REQUIRED_FALSE = new InstanceIdentifierType(false);
 
     /**
      * Constructs new instance identifier.
@@ -50,7 +50,7 @@ public final class InstanceIdentifier implements InstanceIdentifierTypeDefinitio
      * @deprecated Use {@link #getInstance()} for default one, since Instance Identifier does not have xpath.
      */
     @Deprecated
-    public InstanceIdentifier(final RevisionAwareXPath xpath) {
+    public InstanceIdentifierType(final RevisionAwareXPath xpath) {
         requireInstance = true;
     }
 
@@ -62,19 +62,19 @@ public final class InstanceIdentifier implements InstanceIdentifierTypeDefinitio
      * @deprecated Use {@link #create(boolean)}, since Instance Identifier does not have xpath.
      */
     @Deprecated
-    public InstanceIdentifier(final RevisionAwareXPath xpath, final boolean requireInstance) {
+    public InstanceIdentifierType(final RevisionAwareXPath xpath, final boolean requireInstance) {
         this.requireInstance = requireInstance;
     }
 
-    private InstanceIdentifier(final boolean requiredInstance) {
+    private InstanceIdentifierType(final boolean requiredInstance) {
         this.requireInstance = requiredInstance;
     }
 
-    public static InstanceIdentifier getInstance() {
+    public static InstanceIdentifierType getInstance() {
         return INSTANCE_WITH_REQUIRED_TRUE;
     }
 
-    public static InstanceIdentifier create(final boolean requireInstance) {
+    public static InstanceIdentifierType create(final boolean requireInstance) {
         return requireInstance ? INSTANCE_WITH_REQUIRED_TRUE : INSTANCE_WITH_REQUIRED_FALSE;
     }
 
@@ -216,7 +216,7 @@ public final class InstanceIdentifier implements InstanceIdentifierTypeDefinitio
         if (getClass() != obj.getClass()) {
             return false;
         }
-        InstanceIdentifier other = (InstanceIdentifier) obj;
+        InstanceIdentifierType other = (InstanceIdentifierType) obj;
         return requireInstance.equals(other.requireInstance);
     }
 
index ec30f794b8598e88c4c93e605db3ac87761daafb..7f263193f3d3ea58995dc3ab90529009b41a7d95 100644 (file)
@@ -46,7 +46,7 @@
  *       <dt>uint64</dt>
  *       <dd>64-bit unsigned integer -{@link org.opendaylight.yangtools.yang.model.util.Int64}</dd>
  *       <dt>instance-identifier</dt>
- *       <dd>References a data tree node - {@link org.opendaylight.yangtools.yang.model.util.InstanceIdentifier}</dd>
+ *       <dd>References a data tree node - {@link org.opendaylight.yangtools.yang.model.util.InstanceIdentifierType}</dd>
  *       <dt>string</dt>
  *       <dd>{@link org.opendaylight.yangtools.yang.model.util.StringType}</dd>
  *     </dl>
index f4fb777ccd3c845fce80c5ce8c63645f34155e3a..ac9358c72eb55494f9affb955ff918747f8126d8 100644 (file)
@@ -108,7 +108,7 @@ END_IDENTIFIER_LEFT_BRACE : '{' ->type(LEFT_BRACE), popMode;
  
 fragment SUB_STRING : ('"' (ESC | ~["])*'"') | ('\'' (ESC | ~['])*'\'') ;
 
-STRING: ((~( '\r' | '\n' | '\t' | ' ' | ';' | '{' | '"' | '\'' )+) | SUB_STRING ) ->popMode;// IDENTIFIER ;
+STRING: ((~( '\r' | '\n' | '\t' | ' ' | ';' | '{' | '"' | '\'')~( '\r' | '\n' | '\t' | ' ' | ';' | '{' )* ) | SUB_STRING ) ->popMode;// IDENTIFIER ;
 S : [ \n\r\t] -> skip;    
 
 mode BLOCK_COMMENT_MODE;
index 13f5dbba5ec14bc6d500d0033068b7dff8358473..c7054fbc9f41120bdf92f96f52942b99444a63f4 100644 (file)
@@ -29,9 +29,9 @@ unknown_statement : (YIN_ELEMENT_KEYWORD | YANG_VERSION_KEYWORD | WHEN_KEYWORD |
                     NAMESPACE_KEYWORD | MUST_KEYWORD | MODULE_KEYWORD | MIN_ELEMENTS_KEYWORD | MAX_ELEMENTS_KEYWORD | MANDATORY_KEYWORD | LIST_KEYWORD | 
                     LENGTH_KEYWORD | LEAF_LIST_KEYWORD | LEAF_KEYWORD | KEY_KEYWORD | INPUT_KEYWORD | INCLUDE_KEYWORD | IMPORT_KEYWORD | IF_FEATURE_KEYWORD | 
                     IDENTITY_KEYWORD | GROUPING_KEYWORD | FRACTION_DIGITS_KEYWORD | FEATURE_KEYWORD | DEVIATE_KEYWORD | DEVIATION_KEYWORD | EXTENSION_KEYWORD | 
-                    ERROR_MESSAGE_KEYWORD | ERROR_APP_TAG_KEYWORD | ENUM_KEYWORD | DESCRIPTION_KEYWORD | DEFAULT_KEYWORD | CONTAINER_KEYWORD | CONTACT_KEYWORD | 
+                    ERROR_MESSAGE_KEYWORD | ERROR_APP_TAG_KEYWORD | ENUM_KEYWORD | DESCRIPTION_KEYWORD | STATUS_KEYWORD | DEFAULT_KEYWORD | CONTAINER_KEYWORD | CONTACT_KEYWORD | 
                     CONFIG_KEYWORD | CHOICE_KEYWORD |  CASE_KEYWORD | BIT_KEYWORD | BELONGS_TO_KEYWORD | BASE_KEYWORD | AUGMENT_KEYWORD |  
-                    ANYXML_KEYWORD | IDENTIFIER) string? (SEMICOLON | (LEFT_BRACE unknown_statement* | identifier_stmt* RIGHT_BRACE)*);
+                    ANYXML_KEYWORD | IDENTIFIER) string? (SEMICOLON | (LEFT_BRACE (unknown_statement | identifier_stmt)* RIGHT_BRACE)*);
 
 stmtend : (SEMICOLON) | (LEFT_BRACE identifier_stmt? RIGHT_BRACE);
 deviate_replace_stmt : DEVIATE_KEYWORD string /* REPLACE_KEYWORD */ (SEMICOLON | (LEFT_BRACE (identifier_stmt |type_stmt | units_stmt | default_stmt | config_stmt | mandatory_stmt | min_elements_stmt | max_elements_stmt )* RIGHT_BRACE));
@@ -63,11 +63,11 @@ short_case_stmt : container_stmt | leaf_stmt | leaf_list_stmt | list_stmt | anyx
 choice_stmt : CHOICE_KEYWORD string (SEMICOLON | (LEFT_BRACE  (identifier_stmt |when_stmt | if_feature_stmt | default_stmt | config_stmt | mandatory_stmt | status_stmt | description_stmt | reference_stmt | short_case_stmt | case_stmt)* RIGHT_BRACE));
 unique_stmt : UNIQUE_KEYWORD string stmtend;
 key_stmt : KEY_KEYWORD string stmtend;
-list_stmt : LIST_KEYWORD string LEFT_BRACE  (identifier_stmt |when_stmt | if_feature_stmt | must_stmt | key_stmt | unique_stmt | config_stmt | min_elements_stmt | max_elements_stmt | ordered_by_stmt | status_stmt | description_stmt | reference_stmt | typedef_stmt | grouping_stmt | data_def_stmt )* RIGHT_BRACE;
+list_stmt : LIST_KEYWORD string LEFT_BRACE  (when_stmt | if_feature_stmt | must_stmt | key_stmt | unique_stmt | config_stmt | min_elements_stmt | max_elements_stmt | ordered_by_stmt | status_stmt | description_stmt | reference_stmt | typedef_stmt | grouping_stmt | data_def_stmt | identifier_stmt)* RIGHT_BRACE;
 leaf_list_stmt : LEAF_LIST_KEYWORD string LEFT_BRACE  (identifier_stmt |when_stmt | if_feature_stmt | type_stmt | units_stmt | must_stmt | config_stmt | min_elements_stmt | max_elements_stmt | ordered_by_stmt | status_stmt | description_stmt | reference_stmt )* RIGHT_BRACE;
 leaf_stmt : LEAF_KEYWORD string LEFT_BRACE  (identifier_stmt |when_stmt | if_feature_stmt | type_stmt | units_stmt | must_stmt | default_stmt | config_stmt | mandatory_stmt | status_stmt | description_stmt | reference_stmt )* RIGHT_BRACE;
-container_stmt : CONTAINER_KEYWORD string (SEMICOLON | (LEFT_BRACE  (identifier_stmt | when_stmt | if_feature_stmt | must_stmt | presence_stmt | config_stmt | status_stmt | description_stmt | reference_stmt | typedef_stmt | grouping_stmt | data_def_stmt )* RIGHT_BRACE));
-grouping_stmt : GROUPING_KEYWORD string (SEMICOLON | (LEFT_BRACE  (identifier_stmt |status_stmt | description_stmt | reference_stmt | typedef_stmt | grouping_stmt | data_def_stmt )* RIGHT_BRACE));
+container_stmt : CONTAINER_KEYWORD string (SEMICOLON | (LEFT_BRACE  (when_stmt | if_feature_stmt | must_stmt | presence_stmt | config_stmt | status_stmt | description_stmt | reference_stmt | typedef_stmt | grouping_stmt | data_def_stmt | identifier_stmt)* RIGHT_BRACE));
+grouping_stmt : GROUPING_KEYWORD string (SEMICOLON | (LEFT_BRACE (status_stmt | description_stmt | reference_stmt | typedef_stmt | grouping_stmt | data_def_stmt | identifier_stmt)* RIGHT_BRACE));
 value_stmt : VALUE_KEYWORD string stmtend;
 max_value_arg : /*UNBOUNDED_KEYWORD |*/ string;
 min_value_arg : /*UNBOUNDED_KEYWORD |*/ string;
index b8fb5b4a17d4894049f23e8f4222ca2637b2f3e6..3ba54e404f44680010e47722baf848e4b993f590 100644 (file)
@@ -13,14 +13,12 @@ import com.google.common.base.CharMatcher;
 import com.google.common.base.Optional;
 import com.google.common.base.Splitter;
 import com.google.common.collect.Lists;
-
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
-
 import org.antlr.v4.runtime.ParserRuleContext;
 import org.antlr.v4.runtime.tree.ParseTree;
 import org.antlr.v4.runtime.tree.TerminalNode;
@@ -107,7 +105,7 @@ import org.opendaylight.yangtools.yang.model.util.BitsType;
 import org.opendaylight.yangtools.yang.model.util.Decimal64;
 import org.opendaylight.yangtools.yang.model.util.EnumerationType;
 import org.opendaylight.yangtools.yang.model.util.ExtendedType;
-import org.opendaylight.yangtools.yang.model.util.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.util.InstanceIdentifierType;
 import org.opendaylight.yangtools.yang.model.util.Int16;
 import org.opendaylight.yangtools.yang.model.util.Int32;
 import org.opendaylight.yangtools.yang.model.util.Int64;
@@ -1071,10 +1069,12 @@ public final class ParserListenerUtils {
 
             if (parent instanceof TypeDefinitionBuilder) {
                 TypeDefinitionBuilder typedef = (TypeDefinitionBuilder) parent;
-                typedef.setRanges(rangeStatements);
-                typedef.setLengths(lengthStatements);
-                typedef.setPatterns(patternStatements);
-                typedef.setFractionDigits(fractionDigits);
+                if (!(typedef instanceof UnionTypeBuilder)) {
+                    typedef.setRanges(rangeStatements);
+                    typedef.setLengths(lengthStatements);
+                    typedef.setPatterns(patternStatements);
+                    typedef.setFractionDigits(fractionDigits);
+                }
                 return unknownType.build();
             } else {
                 TypeDefinition<?> baseType = unknownType.build();
@@ -1210,7 +1210,7 @@ public final class ParserListenerUtils {
             constraints.addLengths(binaryType.getLengthConstraints());
             baseType = binaryType;
         } else if ("instance-identifier".equals(typeName)) {
-            return InstanceIdentifier.create(isRequireInstance(typeBody));
+            return InstanceIdentifierType.create(isRequireInstance(typeBody));
         }
 
         if (parent instanceof TypeDefinitionBuilder && !(parent instanceof UnionTypeBuilder)) {
@@ -1428,7 +1428,12 @@ public final class ParserListenerUtils {
             for (int i = 0; i < ctx.getChildCount(); i++) {
                 ParseTree maxArg = ctx.getChild(i);
                 if (maxArg instanceof Max_value_argContext) {
-                    result = Integer.valueOf(stringFromNode(maxArg));
+                    String maxValue = stringFromNode(maxArg);
+                    if ("unbounded".equals(maxValue)) {
+                        result = Integer.MAX_VALUE;
+                    } else {
+                        result = Integer.valueOf(maxValue);
+                    }
                 }
             }
             if (result == null) {
index 5805cd7e8ccf4e5ce575ea003c001b0ea9000147..133c7b18a75d3b0d736607939086ddd5770016eb 100644 (file)
@@ -8,10 +8,12 @@
 package org.opendaylight.yangtools.yang.parser.impl;
 
 import com.google.common.collect.Sets;
+
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.HashSet;
 import java.util.Set;
+
 import org.antlr.v4.runtime.tree.ParseTree;
 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser;
 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Anyxml_stmtContext;
@@ -66,7 +68,7 @@ import org.slf4j.LoggerFactory;
  * This validator expects only one module or submodule per file and performs
  * only basic validation where context from all yang models is not present.
  */
-final class YangModelBasicValidationListener extends YangParserBaseListener {
+public final class YangModelBasicValidationListener extends YangParserBaseListener {
     private static final Logger LOGGER = LoggerFactory.getLogger(YangModelBasicValidationListener.class);
     private final Set<String> uniquePrefixes = new HashSet<>();
     private final Set<String> uniqueImports = new HashSet<>();
index 0002c11187b5a723c482e85ca5efdc194c8430ef..1d0d48a7ddf2d7b5c034440d15f477c76b29e8fd 100644 (file)
@@ -22,6 +22,7 @@ import com.google.common.base.Preconditions;
 import com.google.common.base.Splitter;
 import com.google.common.collect.HashBiMap;
 import com.google.common.io.ByteSource;
+
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -37,7 +38,9 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeMap;
+
 import javax.annotation.concurrent.Immutable;
+
 import org.antlr.v4.runtime.ANTLRInputStream;
 import org.antlr.v4.runtime.CommonTokenStream;
 import org.antlr.v4.runtime.tree.ParseTree;
@@ -107,8 +110,7 @@ public final class YangParserImpl implements YangContextParser {
     }
 
     @Override
-    public SchemaContext parseFile(final File yangFile, final File directory) throws IOException,
-    YangSyntaxErrorException {
+    public SchemaContext parseFile(final File yangFile, final File directory) throws IOException, YangSyntaxErrorException {
         Preconditions.checkState(yangFile.exists(), yangFile + " does not exists");
         Preconditions.checkState(directory.exists(), directory + " does not exists");
         Preconditions.checkState(directory.isDirectory(), directory + " is not a directory");
@@ -194,12 +196,8 @@ public final class YangParserImpl implements YangContextParser {
     }
 
     @Override
-    public SchemaContext parseSources(final Collection<ByteSource> sources) throws IOException,
-    YangSyntaxErrorException {
-        Collection<Module> unsorted = parseYangModelSources(sources).values();
-        Set<Module> sorted = new LinkedHashSet<>(
-                ModuleDependencySort.sort(unsorted.toArray(new Module[unsorted.size()])));
-        return resolveSchemaContext(sorted);
+    public SchemaContext parseSources(final Collection<ByteSource> sources) throws IOException,YangSyntaxErrorException {
+        return assembleContext(parseYangModelSources(sources).values());
     }
 
     @Override
@@ -236,7 +234,7 @@ public final class YangParserImpl implements YangContextParser {
         return resolveSchemaContext(result);
     }
 
-    private LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> resolveModulesWithImports(final List<ModuleBuilder> sorted,
+    private static LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> resolveModulesWithImports(final List<ModuleBuilder> sorted,
             final SchemaContext context) {
         final LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> modules = orderModules(sorted);
         for (ModuleBuilder module : sorted) {
@@ -335,8 +333,21 @@ public final class YangParserImpl implements YangContextParser {
         return new SchemaContextImpl(modules, identifiersToSources);
     }
 
-    private Map<ByteSource, Module> parseYangModelSources(final Collection<ByteSource> sources) throws IOException,
-    YangSyntaxErrorException {
+    public Collection<Module> buildModules(final Collection<ModuleBuilder> builders) {
+        List<ModuleBuilder> sorted = ModuleDependencySort.sort(builders);
+        Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModulesWithImports(sorted, null);
+        Map<ModuleBuilder, Module> builderToModule = build(modules);
+
+        return builderToModule.values();
+    }
+
+    public SchemaContext assembleContext(final Collection<Module> modules) {
+        final Set<Module> sorted = new LinkedHashSet<>(
+                ModuleDependencySort.sort(modules.toArray(new Module[modules.size()])));
+        return resolveSchemaContext(sorted);
+    }
+
+    private Map<ByteSource, Module> parseYangModelSources(final Collection<ByteSource> sources) throws IOException, YangSyntaxErrorException {
         if (sources == null || sources.isEmpty()) {
             return Collections.emptyMap();
         }
@@ -368,8 +379,7 @@ public final class YangParserImpl implements YangContextParser {
      * @throws YangSyntaxErrorException
      */
     // TODO: remove ByteSource result after removing YangModelParser
-    private Map<ByteSource, ModuleBuilder> resolveSources(final Collection<ByteSource> streams) throws IOException,
-    YangSyntaxErrorException {
+    private Map<ByteSource, ModuleBuilder> resolveSources(final Collection<ByteSource> streams) throws IOException, YangSyntaxErrorException {
         Map<ByteSource, ModuleBuilder> builders = parseSourcesToBuilders(streams);
         return resolveSubmodules(builders);
     }
@@ -511,7 +521,7 @@ public final class YangParserImpl implements YangContextParser {
      *            topologically sorted modules
      * @return modules ordered by name and revision
      */
-    private LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> orderModules(final List<ModuleBuilder> modules) {
+    private static LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> orderModules(final List<ModuleBuilder> modules) {
         final LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> result = new LinkedHashMap<>();
         for (final ModuleBuilder builder : modules) {
             if (builder == null) {
@@ -583,31 +593,29 @@ public final class YangParserImpl implements YangContextParser {
         }
     }
 
-    private Map<ByteSource, ParseTree> parseYangSources(final Collection<ByteSource> sources) throws IOException,
-    YangSyntaxErrorException {
+    private Map<ByteSource, ParseTree> parseYangSources(final Collection<ByteSource> sources) throws IOException, YangSyntaxErrorException {
         final Map<ByteSource, ParseTree> trees = new HashMap<>();
         for (ByteSource source : sources) {
-            trees.put(source, parseYangSource(source));
+            try (InputStream stream = source.openStream()) {
+                trees.put(source, parseYangSource(stream));
+            }
         }
         return trees;
     }
 
-    private YangContext parseYangSource(final ByteSource source) throws IOException, YangSyntaxErrorException {
-        try (InputStream stream = source.openStream()) {
-            final ANTLRInputStream input = new ANTLRInputStream(stream);
-            final YangLexer lexer = new YangLexer(input);
-            final CommonTokenStream tokens = new CommonTokenStream(lexer);
-            final YangParser parser = new YangParser(tokens);
-            parser.removeErrorListeners();
+    public static YangContext parseYangSource(final InputStream stream) throws IOException, YangSyntaxErrorException {
+        final YangLexer lexer = new YangLexer(new ANTLRInputStream(stream));
+        final CommonTokenStream tokens = new CommonTokenStream(lexer);
+        final YangParser parser = new YangParser(tokens);
+        parser.removeErrorListeners();
 
-            final YangErrorListener errorListener = new YangErrorListener();
-            parser.addErrorListener(errorListener);
+        final YangErrorListener errorListener = new YangErrorListener();
+        parser.addErrorListener(errorListener);
 
-            final YangContext result = parser.yang();
-            errorListener.validate();
+        final YangContext result = parser.yang();
+        errorListener.validate();
 
-            return result;
-        }
+        return result;
     }
 
     /**
@@ -1276,5 +1284,4 @@ public final class YangParserImpl implements YangContextParser {
         }
         dev.setTargetPath(((SchemaNodeBuilder) currentParent).getPath());
     }
-
 }
index 50354f5ed8956fb29d03520f7affdfdeee54c2d7..45bedd8c3a9b7da64a762d74a141549adc13065a 100644 (file)
@@ -26,6 +26,7 @@ import static org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils.st
 import com.google.common.base.Splitter;
 import com.google.common.base.Strings;
 import com.google.common.collect.Iterables;
+
 import java.net.URI;
 import java.text.DateFormat;
 import java.text.ParseException;
@@ -33,7 +34,9 @@ import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.Iterator;
 import java.util.List;
+
 import org.antlr.v4.runtime.tree.ParseTree;
+import org.antlr.v4.runtime.tree.ParseTreeWalker;
 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser;
 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Argument_stmtContext;
 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Base_stmtContext;
@@ -96,6 +99,7 @@ import org.opendaylight.yangtools.yang.parser.builder.impl.UnknownSchemaNodeBuil
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+
 public final class YangParserListenerImpl extends YangParserBaseListener {
     private static final Logger LOG = LoggerFactory.getLogger(YangParserListenerImpl.class);
     private static final Splitter COLON_SPLITTER = Splitter.on(':');
@@ -113,6 +117,24 @@ public final class YangParserListenerImpl extends YangParserBaseListener {
         this.sourcePath = sourcePath;
     }
 
+    /**
+     * Create a new instance.
+     *
+     * FIXME: the resulting type needs to be extracted, such that we can reuse
+     *        the "BaseListener" aspect, which need not be exposed to the user.
+     *        Maybe factor out a base class into repo.spi?
+     *
+     * @param sourcePath
+     * @param walker
+     * @param tree
+     * @return
+     */
+    public static YangParserListenerImpl create(final String sourcePath, final ParseTreeWalker walker, final ParseTree tree) {
+        final YangParserListenerImpl ret = new YangParserListenerImpl(sourcePath);
+        walker.walk(ret, tree);
+        return ret;
+    }
+
     @Override
     public void enterModule_stmt(final YangParser.Module_stmtContext ctx) {
         moduleName = stringFromNode(ctx);
index fcf0b8924c15fc3ab81a6e908b4d77173683f412..97590779b2a75934a46c8f6d19ad1f015c04ef17 100644 (file)
@@ -11,9 +11,12 @@ import static org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils.ge
 
 import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableSet;
+
 import java.io.InputStream;
 import java.util.Date;
 import java.util.List;
+
+import org.antlr.v4.runtime.ParserRuleContext;
 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Belongs_to_stmtContext;
 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Import_stmtContext;
 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Include_stmtContext;
@@ -25,6 +28,7 @@ import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Submodule_stmtCont
 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.YangContext;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
 import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
 
 /**
@@ -53,7 +57,7 @@ public abstract class YangModelDependencyInfo {
     private final ImmutableSet<ModuleImport> dependencies;
 
     YangModelDependencyInfo(final String name, final String formattedRevision,
-                            final ImmutableSet<ModuleImport> imports, final ImmutableSet<ModuleImport> includes) {
+            final ImmutableSet<ModuleImport> imports, final ImmutableSet<ModuleImport> includes) {
         this.name = name;
         this.formattedRevision = formattedRevision;
         this.revision = QName.parseRevision(formattedRevision);
@@ -142,6 +146,29 @@ public abstract class YangModelDependencyInfo {
         return true;
     }
 
+    /**
+     * Extracts {@link YangModelDependencyInfo} from an abstract syntax tree
+     * of a YANG model.
+     *
+     * @param tree Abstract syntax tree
+     * @return {@link YangModelDependencyInfo}
+     * @throws YangSyntaxErrorException
+     *             If the AST is not a valid YANG module/submodule
+     */
+    public static YangModelDependencyInfo fromAST(final String name, final ParserRuleContext tree) throws YangSyntaxErrorException {
+        final Optional<Module_stmtContext> moduleCtx = getFirstContext(tree, Module_stmtContext.class);
+        if (moduleCtx.isPresent()) {
+            return parseModuleContext(moduleCtx.get());
+        }
+
+        final Optional<Submodule_stmtContext> submoduleCtx = getFirstContext(tree, Submodule_stmtContext.class);
+        if (submoduleCtx.isPresent()) {
+            return parseSubmoduleContext(submoduleCtx.get());
+        }
+
+        throw new YangSyntaxErrorException(name, 0, 0, "Unknown YANG text type");
+    }
+
     /**
      * Extracts {@link YangModelDependencyInfo} from input stream
      * containing YANG model.
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/AbstractURLRegistration.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/AbstractURLRegistration.java
new file mode 100644 (file)
index 0000000..8dee836
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * 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.parser.repo;
+
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+
+public abstract class AbstractURLRegistration extends AbstractObjectRegistration<YangTextSchemaSource> implements URLRegistration{
+    protected AbstractURLRegistration(final YangTextSchemaSource text) {
+        super(text);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/DependencyResolver.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/DependencyResolver.java
new file mode 100644 (file)
index 0000000..5bc6616
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * 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.parser.repo;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.impl.util.YangModelDependencyInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Inter-module dependency resolved. Given a set of schema source identifiers and their
+ * corresponding dependency information, the {@link #create(Map)} method creates a
+ * a view of how consistent the dependencies are. In particular, this detects whether
+ * any imports are unsatisfied.
+ *
+ * FIXME: improve this class to track and expose how wildcard imports were resolved.
+ *        That information will allow us to track "damage" to dependency resolution
+ *        as new models are added to a schema context.
+ */
+final class DependencyResolver {
+    private static final Logger LOG = LoggerFactory.getLogger(DependencyResolver.class);
+    private final Collection<SourceIdentifier> resolvedSources;
+    private final Collection<SourceIdentifier> unresolvedSources;
+    private final Multimap<SourceIdentifier, ModuleImport> unsatisfiedImports;
+
+    public DependencyResolver(final Collection<SourceIdentifier> resolvedSources,
+            final Collection<SourceIdentifier> unresolvedSources, final Multimap<SourceIdentifier, ModuleImport> unsatisfiedImports) {
+        this.resolvedSources = Preconditions.checkNotNull(resolvedSources);
+        this.unresolvedSources = Preconditions.checkNotNull(unresolvedSources);
+        this.unsatisfiedImports = Preconditions.checkNotNull(unsatisfiedImports);
+    }
+
+    private static SourceIdentifier findWildcard(final Iterable<SourceIdentifier> haystack, final String needle) {
+        for (SourceIdentifier r : haystack) {
+            if (r.getName().equals(needle)) {
+                return r;
+            }
+        }
+
+        return null;
+    }
+
+    private static boolean isKnown(final Collection<SourceIdentifier> haystack, final ModuleImport mi) {
+        final String rev = mi.getRevision() != null ? mi.getRevision().toString() : null;
+        final SourceIdentifier msi = SourceIdentifier.create(mi.getModuleName(), Optional.fromNullable(rev));
+
+        // Quick lookup
+        if (haystack.contains(msi)) {
+            return true;
+        }
+
+        // Slow revision-less walk
+        return rev == null && findWildcard(haystack, mi.getModuleName()) != null;
+    }
+
+
+
+    public static final DependencyResolver create(final Map<SourceIdentifier, YangModelDependencyInfo> depInfo) {
+        final Collection<SourceIdentifier> resolved = new ArrayList<>(depInfo.size());
+        final Collection<SourceIdentifier> pending = new ArrayList<>(depInfo.keySet());
+
+        boolean progress;
+        do {
+            progress = false;
+
+            final Iterator<SourceIdentifier> it = pending.iterator();
+            while (it.hasNext()) {
+                final SourceIdentifier id = it.next();
+                final YangModelDependencyInfo dep = depInfo.get(id);
+
+                boolean okay = true;
+                for (ModuleImport mi : dep.getDependencies()) {
+                    if (!isKnown(resolved, mi)) {
+                        LOG.debug("Source {} is missing import {}", id, mi);
+                        okay = false;
+                        break;
+                    }
+                }
+
+                if (okay) {
+                    LOG.debug("Resolved source {}", id);
+                    resolved.add(id);
+                    it.remove();
+                    progress = true;
+                }
+            }
+        } while (progress);
+
+        if (!pending.isEmpty()) {
+            final Multimap<SourceIdentifier, ModuleImport> imports = ArrayListMultimap.create();
+            for (SourceIdentifier id : pending) {
+                final YangModelDependencyInfo dep = depInfo.get(id);
+                for (ModuleImport mi : dep.getDependencies()) {
+                    if (!isKnown(pending, mi) && !isKnown(resolved, mi)) {
+                        imports.put(id, mi);
+                    }
+                }
+            }
+
+            return new DependencyResolver(resolved, pending, imports);
+        } else {
+            return new DependencyResolver(resolved, Collections.<SourceIdentifier>emptyList(), ImmutableMultimap.<SourceIdentifier, ModuleImport>of());
+        }
+    }
+
+    /**
+     * Collection of sources which have been resolved.
+     *
+     * @return
+     */
+    Collection<SourceIdentifier> getResolvedSources() {
+        return resolvedSources;
+    }
+
+    /**
+     * Collection of sources which have not been resolved due to missing dependencies.
+     *
+     * @return
+     */
+    Collection<SourceIdentifier> getUnresolvedSources() {
+        return unresolvedSources;
+    }
+
+    /**
+     * Detailed information about which imports were missing. The key in the map
+     * is the source identifier of module which was issuing an import, the values
+     * are imports which were unsatisfied.
+     *
+     * Note that this map contains only imports which are missing from the reactor,
+     * not transitive failures.
+     *
+     * Examples:
+     *
+     * If A imports B, B imports C, and both A and B are in the reactor, only B->C
+     * will be reported.
+     *
+     * If A imports B and C, B imports C, and both A and B are in the reactor,
+     * A->C and B->C will be reported.
+     *
+     * @return
+     */
+    Multimap<SourceIdentifier, ModuleImport> getUnsatisfiedImports() {
+        return unsatisfiedImports;
+    }
+}
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaContextFactory.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaContextFactory.java
new file mode 100644 (file)
index 0000000..5873856
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * 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.parser.repo;
+
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Maps;
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.antlr.v4.runtime.ParserRuleContext;
+import org.antlr.v4.runtime.tree.ParseTreeWalker;
+import org.opendaylight.yangtools.util.concurrent.ExceptionMapper;
+import org.opendaylight.yangtools.util.concurrent.ReflectiveExceptionMapper;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaResolutionException;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserListenerImpl;
+import org.opendaylight.yangtools.yang.parser.impl.util.YangModelDependencyInfo;
+import org.opendaylight.yangtools.yang.parser.util.ASTSchemaSource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class SharedSchemaContextFactory implements SchemaContextFactory {
+    private static final ExceptionMapper<SchemaResolutionException> MAPPER = ReflectiveExceptionMapper.create("resolve sources", SchemaResolutionException.class);
+    private static final Logger LOG = LoggerFactory.getLogger(SharedSchemaContextFactory.class);
+
+    private final Function<SourceIdentifier, ListenableFuture<ASTSchemaSource>> requestSources = new Function<SourceIdentifier, ListenableFuture<ASTSchemaSource>>() {
+        @Override
+        public ListenableFuture<ASTSchemaSource> apply(final SourceIdentifier input) {
+            return repository.getSchemaSource(input, ASTSchemaSource.class);
+        }
+    };
+    private final Cache<Collection<SourceIdentifier>, SchemaContext> cache = CacheBuilder.newBuilder().softValues().build();
+
+    private final AsyncFunction<List<ASTSchemaSource>, SchemaContext> assembleSources = new AsyncFunction<List<ASTSchemaSource>, SchemaContext>() {
+        @Override
+        public ListenableFuture<SchemaContext> apply(final List<ASTSchemaSource> sources) throws SchemaResolutionException {
+            final Map<SourceIdentifier, ASTSchemaSource> srcs =
+                    Maps.uniqueIndex(sources, ASTSchemaSource.GET_IDENTIFIER);
+            final Map<SourceIdentifier, YangModelDependencyInfo> deps =
+                    Maps.transformValues(srcs, ASTSchemaSource.GET_DEPINFO);
+
+            LOG.debug("Resolving dependency reactor {}", deps);
+
+            final DependencyResolver res = DependencyResolver.create(deps);
+            if (!res.getUnresolvedSources().isEmpty()) {
+                LOG.debug("Omitting models {} due to unsatisfied imports {}", res.getUnresolvedSources(), res.getUnsatisfiedImports());
+
+                // FIXME: push into DependencyResolver
+
+                throw new SchemaResolutionException("Failed to resolve required models",
+                        res.getResolvedSources(), res.getUnsatisfiedImports());
+            }
+
+            final Map<SourceIdentifier, ParserRuleContext> asts =
+                    Maps.transformValues(srcs, ASTSchemaSource.GET_AST);
+
+            final ParseTreeWalker walker = new ParseTreeWalker();
+            final Map<SourceIdentifier, ModuleBuilder> sourceToBuilder = new LinkedHashMap<>();
+
+            for (Entry<SourceIdentifier, ParserRuleContext> entry : asts.entrySet()) {
+                ModuleBuilder moduleBuilder =
+                        YangParserListenerImpl.create(entry.getKey().getName(), walker, entry.getValue()).getModuleBuilder();
+
+                moduleBuilder.setSource(srcs.get(entry.getKey()).getYangText());
+                sourceToBuilder.put(entry.getKey(), moduleBuilder);
+            }
+            LOG.debug("Modules ready for integration");
+
+            final YangParserImpl parser = YangParserImpl.getInstance();
+            final Collection<Module> modules = parser.buildModules(sourceToBuilder.values());
+            LOG.debug("Integrated cross-references modules");
+            return Futures.immediateCheckedFuture(parser.assembleContext(modules));
+        }
+    };
+
+    private final SharedSchemaRepository repository;
+    // FIXME: ignored right now
+    private final SchemaSourceFilter filter;
+
+    public SharedSchemaContextFactory(final SharedSchemaRepository repository, final SchemaSourceFilter filter) {
+        this.repository = Preconditions.checkNotNull(repository);
+        this.filter = Preconditions.checkNotNull(filter);
+    }
+
+    @Override
+    public CheckedFuture<SchemaContext, SchemaResolutionException> createSchemaContext(final Collection<SourceIdentifier> requiredSources) {
+        final SchemaContext existing = cache.getIfPresent(requiredSources);
+        if (existing != null) {
+            LOG.debug("Returning cached context {}", existing);
+            return Futures.immediateCheckedFuture(existing);
+        }
+
+        // Request all sources be loaded
+        final ListenableFuture<List<ASTSchemaSource>> sf = Futures.allAsList(Collections2.transform(requiredSources, requestSources));
+
+        // Assemble sources into a schemacontext
+        final ListenableFuture<SchemaContext> cf = Futures.transform(sf, assembleSources);
+
+        // Populate cache when successful
+        Futures.addCallback(cf, new FutureCallback<SchemaContext>() {
+            @Override
+            public void onSuccess(final SchemaContext result) {
+                cache.put(requiredSources, result);
+            }
+
+            @Override
+            public void onFailure(final Throwable t) {
+                LOG.info("Failed to assemble sources", t);
+            }
+        });
+
+        return Futures.makeChecked(cf, MAPPER);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaRepository.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaRepository.java
new file mode 100644 (file)
index 0000000..1ff9f32
--- /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.parser.repo;
+
+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.Identifiable;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.util.AbstractSchemaRepository;
+
+/**
+ * A {@link SchemaRepository} which allows sharing of {@link SchemaContext} as
+ * long as their specification is the same.
+ *
+ * Note: for current implementation, "same" means the same filter and the same
+ * set of {@link SourceIdentifier}s.
+ */
+@Beta
+public final class SharedSchemaRepository extends AbstractSchemaRepository implements Identifiable<String> {
+    private final LoadingCache<SchemaSourceFilter, SchemaContextFactory> cache =
+            CacheBuilder.newBuilder().softValues().build(new CacheLoader<SchemaSourceFilter, SchemaContextFactory>() {
+                @Override
+                public SchemaContextFactory load(final SchemaSourceFilter key) {
+                    return new SharedSchemaContextFactory(SharedSchemaRepository.this, key);
+                }
+            });
+    private final String id;
+
+    public SharedSchemaRepository(final String id) {
+        this.id = Preconditions.checkNotNull(id);
+    }
+
+    @Override
+    public String getIdentifier() {
+        return id;
+    }
+
+    @Override
+    public SchemaContextFactory createSchemaContextFactory(final SchemaSourceFilter filter) {
+        return cache.getUnchecked(filter);
+    }
+
+    @Override
+    public String toString() {
+        return "SchemaRepository: " + id;
+    }
+}
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/URLRegistration.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/URLRegistration.java
new file mode 100644 (file)
index 0000000..d49ecc9
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * 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.parser.repo;
+
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+
+public interface URLRegistration extends ObjectRegistration<YangTextSchemaSource> {
+    @Override
+    void close();
+}
\ No newline at end of file
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/URLSchemaContextResolver.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/URLSchemaContextResolver.java
new file mode 100644 (file)
index 0000000..47de6b1
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * 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.parser.repo;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Collection;
+import java.util.concurrent.ConcurrentLinkedDeque;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
+import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaResolutionException;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
+import org.opendaylight.yangtools.yang.parser.util.ASTSchemaSource;
+import org.opendaylight.yangtools.yang.parser.util.TextToASTTransformer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Beta
+public class URLSchemaContextResolver implements SchemaSourceProvider<YangTextSchemaSource> {
+    private static final Logger LOG = LoggerFactory.getLogger(URLSchemaContextResolver.class);
+
+    private final Cache<SourceIdentifier, YangTextSchemaSource> sources = CacheBuilder.newBuilder().build();
+    private final Collection<SourceIdentifier> requiredSources = new ConcurrentLinkedDeque<>();
+    private final AtomicReference<Optional<SchemaContext>> currentSchemaContext =
+            new AtomicReference<>(Optional.<SchemaContext>absent());
+    private final SchemaSourceRegistry registry;
+    private final SchemaRepository repository;
+    private volatile Object version = new Object();
+    private volatile Object contextVersion = version;
+
+    private URLSchemaContextResolver(final SchemaRepository repository, final SchemaSourceRegistry registry) {
+        this.repository = Preconditions.checkNotNull(repository);
+        this.registry = Preconditions.checkNotNull(registry);
+    }
+
+    public static URLSchemaContextResolver create(final String name) {
+        final SharedSchemaRepository sharedRepo = new SharedSchemaRepository(name);
+        return new URLSchemaContextResolver(sharedRepo, sharedRepo);
+    }
+
+    /**
+     * Register a URL hosting a YANG Text file.
+     *
+     * @param url URL
+     * @throws YangSyntaxErrorException When the YANG file is syntactically invalid
+     * @throws IOException when the URL is not readable
+     * @throws SchemaSourceException When parsing encounters general error
+     */
+    public URLRegistration registerSource(final URL url) throws SchemaSourceException, IOException, YangSyntaxErrorException {
+        checkArgument(url != null, "Supplied URL must not be null");
+
+        final SourceIdentifier guessedId = new SourceIdentifier(url.getFile(), Optional.<String>absent());
+        final YangTextSchemaSource text = new YangTextSchemaSource(guessedId) {
+            @Override
+            public InputStream openStream() throws IOException {
+                return url.openStream();
+            }
+
+            @Override
+            protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+                return toStringHelper.add("url", url);
+            }
+        };
+
+        final ASTSchemaSource ast = TextToASTTransformer.TRANSFORMATION.apply(text).checkedGet();
+        LOG.trace("Resolved URL {} to source {}", url, ast);
+
+        final SourceIdentifier resolvedId = ast.getIdentifier();
+        final SchemaSourceRegistration<YangTextSchemaSource> reg = registry.registerSchemaSource(this,
+                PotentialSchemaSource.create(resolvedId, YangTextSchemaSource.class, 0));
+
+        requiredSources.add(resolvedId);
+        LOG.trace("Added source {} to schema context requirements", resolvedId);
+        version = new Object();
+
+        return new AbstractURLRegistration(text) {
+            @Override
+            protected void removeRegistration() {
+                requiredSources.remove(resolvedId);
+                LOG.trace("Removed source {} from schema context requirements", resolvedId);
+                version = new Object();
+                reg.close();
+                sources.invalidate(resolvedId);
+            }
+        };
+    }
+
+    /**
+     * Try to parse all currently available yang files and build new schema context.
+     * @return new schema context iif there is at least 1 yang file registered and
+     *         new schema context was successfully built.
+     */
+    public Optional<SchemaContext> getSchemaContext() {
+        final SchemaContextFactory factory = repository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
+        Optional<SchemaContext> sc;
+        Object v;
+        do {
+            // Spin get stable context version
+            Object cv;
+            do {
+                cv = contextVersion;
+                sc = currentSchemaContext.get();
+                if (version == cv) {
+                    return sc;
+                }
+            } while (cv != contextVersion);
+
+            // Version has been updated
+            Collection<SourceIdentifier> sources;
+            do {
+                v = version;
+                sources = ImmutableList.copyOf(requiredSources);
+            } while (v != version);
+
+            while (true) {
+                final CheckedFuture<SchemaContext, SchemaResolutionException> f = factory.createSchemaContext(sources);
+                try {
+                    sc = Optional.of(f.checkedGet());
+                    break;
+                } catch (SchemaResolutionException e) {
+                    LOG.info("Failed to fully assemble schema context for {}", sources, e);
+                    sources = e.getResolvedSources();
+                }
+            }
+
+            synchronized (this) {
+                if (contextVersion == cv) {
+                    currentSchemaContext.set(sc);
+                    contextVersion = v;
+                }
+            }
+        } while (version == v);
+
+        return sc;
+    }
+
+    @Override
+    public CheckedFuture<YangTextSchemaSource, SchemaSourceException> getSource(final SourceIdentifier sourceIdentifier) {
+        final YangTextSchemaSource ret = sources.getIfPresent(sourceIdentifier);
+        if (ret == null) {
+            return Futures.<YangTextSchemaSource, SchemaSourceException>immediateFailedCheckedFuture(
+                    new MissingSchemaSourceException("URL for " + sourceIdentifier + " not registered"));
+        }
+
+        return Futures.immediateCheckedFuture(ret);
+    }
+}
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/util/ASTSchemaSource.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/util/ASTSchemaSource.java
new file mode 100644 (file)
index 0000000..5ff2459
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * 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.parser.util;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+
+import javax.annotation.Nonnull;
+
+import org.antlr.v4.runtime.ParserRuleContext;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.impl.util.YangModelDependencyInfo;
+
+/**
+ * Abstract Syntax Tree representation of a schema source. This representation
+ * is internal to the YANG parser implementation, as it relies on ANTLR types.
+ *
+ * Instances of this representation are used for caching purposes, as they
+ * are a natural intermediate step in YANG text processing pipeline: the text
+ * has been successfully parsed, so we know it is syntactically correct. It also
+ * passes basic semantic validation and we were able to extract dependency
+ * information.
+ */
+@Beta
+public final class ASTSchemaSource implements SchemaSourceRepresentation {
+    public static final Function<ASTSchemaSource, SourceIdentifier> GET_IDENTIFIER = new Function<ASTSchemaSource, SourceIdentifier>() {
+        @Override
+        public SourceIdentifier apply(final ASTSchemaSource input) {
+            return input.getIdentifier();
+        }
+    };
+    public static final Function<ASTSchemaSource, YangModelDependencyInfo> GET_DEPINFO = new Function<ASTSchemaSource, YangModelDependencyInfo>() {
+        @Override
+        public YangModelDependencyInfo apply(final ASTSchemaSource input) {
+            return input.getDependencyInformation();
+        }
+    };
+    public static final Function<ASTSchemaSource, ParserRuleContext> GET_AST = new Function<ASTSchemaSource, ParserRuleContext>() {
+        @Override
+        public ParserRuleContext apply(final ASTSchemaSource input) {
+            return input.getAST();
+        }
+    };
+
+    private final YangModelDependencyInfo depInfo;
+    private final ParserRuleContext tree;
+    private final SourceIdentifier id;
+    private final String text;
+
+    private ASTSchemaSource(final @Nonnull SourceIdentifier id, @Nonnull final ParserRuleContext tree, final @Nonnull YangModelDependencyInfo depInfo, final @Nonnull String text) {
+        this.depInfo = Preconditions.checkNotNull(depInfo);
+        this.tree = Preconditions.checkNotNull(tree);
+        this.id = Preconditions.checkNotNull(id);
+        this.text = Preconditions.checkNotNull(text);
+    }
+
+    /**
+     * Create a new instance of AST representation for a abstract syntax tree,
+     * performing minimal semantic analysis to acquire dependency information.
+     *
+     * @param name YANG source name. Used only for error reporting.
+     * @param tree ANTLR abstract syntax tree
+     * @return A new representation instance.
+     * @throws YangSyntaxErrorException if we fail to extract dependency information.
+     */
+    public static final ASTSchemaSource create(final @Nonnull String name, final @Nonnull ParserRuleContext tree) throws YangSyntaxErrorException {
+        final YangModelDependencyInfo depInfo = YangModelDependencyInfo.fromAST(name, tree);
+        final SourceIdentifier id = new SourceIdentifier(depInfo.getName(), Optional.of(depInfo.getFormattedRevision()));
+        return new ASTSchemaSource(id, tree, depInfo, null);
+    }
+
+    /**
+     * Create a new instance of AST representation for a abstract syntax tree,
+     * performing minimal semantic analysis to acquire dependency information.
+     *
+     * @param name YANG source name. Used only for error reporting.
+     * @param tree ANTLR abstract syntax tree
+     * @return A new representation instance.
+     * @throws YangSyntaxErrorException if we fail to extract dependency information.
+     *
+     * @deprecated Migration only, will be removed as soon as the migration is completed.
+     */
+    @Deprecated
+    public static final ASTSchemaSource create(final @Nonnull String name, final @Nonnull ParserRuleContext tree, final String text) throws YangSyntaxErrorException {
+        final YangModelDependencyInfo depInfo = YangModelDependencyInfo.fromAST(name, tree);
+        final SourceIdentifier id = new SourceIdentifier(depInfo.getName(), Optional.of(depInfo.getFormattedRevision()));
+        return new ASTSchemaSource(id, tree, depInfo, text);
+    }
+
+
+    @Override
+    public SourceIdentifier getIdentifier() {
+        return id;
+    }
+
+    @Override
+    public Class<? extends SchemaSourceRepresentation> getType() {
+        return ASTSchemaSource.class;
+    }
+
+    /**
+     * Return the underlying abstract syntax tree.
+     *
+     * @return Underlying AST.
+     */
+    public @Nonnull ParserRuleContext getAST() {
+        return tree;
+    }
+
+    /**
+     * Return the dependency information as extracted from the AST.
+     *
+     * FIXME: this method should be extracted into a public interface in the
+     *        model.api.repo class, relying solely on model.api types.
+     *
+     * @return Dependency information.
+     */
+    public @Nonnull YangModelDependencyInfo getDependencyInformation() {
+        return depInfo;
+    }
+
+    /**
+     * Return the semantically-equivalent text YANG text source.
+     *
+     * @return YANG text source
+     * @deprecated Used for migration purposes. Users are advised to use the
+     *             schema repository to acquire the representation of their
+     *             choice. Will be removed as soon as the migration is completed.
+     */
+    @Deprecated
+    public @Nonnull String getYangText() {
+        return text;
+    }
+}
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/util/TextToASTTransformer.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/util/TextToASTTransformer.java
new file mode 100644 (file)
index 0000000..211e6a2
--- /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.yang.parser.util;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Charsets;
+import com.google.common.io.CharStreams;
+import com.google.common.io.InputSupplier;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.antlr.v4.runtime.tree.ParseTreeWalker;
+import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.YangContext;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
+import org.opendaylight.yangtools.yang.model.repo.util.SchemaSourceTransformer;
+import org.opendaylight.yangtools.yang.parser.impl.YangModelBasicValidationListener;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A {@link SchemaSourceTransformer} which handles translation of models from
+ * {@link YangTextSchemaSource} representation into {@link ASTSchemaSource}.
+ */
+@Beta
+public final class TextToASTTransformer extends SchemaSourceTransformer<YangTextSchemaSource, ASTSchemaSource> {
+    public static final class TextToASTTransformation implements Transformation<YangTextSchemaSource, ASTSchemaSource> {
+        @Override
+        public CheckedFuture<ASTSchemaSource, SchemaSourceException> apply(final YangTextSchemaSource input) throws IOException, YangSyntaxErrorException {
+            try (InputStream is = input.openStream()) {
+                final YangContext ctx = YangParserImpl.parseYangSource(is);
+                LOG.debug("Model {} parsed successfully", input);
+
+                final ParseTreeWalker walker = new ParseTreeWalker();
+                final YangModelBasicValidationListener validator = new YangModelBasicValidationListener();
+                walker.walk(validator, ctx);
+                LOG.debug("Model {} validated successfully", input);
+
+                // Backwards compatibility
+                final String text = CharStreams.toString(
+                        CharStreams.newReaderSupplier(new InputSupplier<InputStream>() {
+                            @Override
+                            public InputStream getInput() throws IOException {
+                                return input.openStream();
+                            }
+                        }, Charsets.UTF_8));
+
+                return Futures.immediateCheckedFuture(ASTSchemaSource.create(input.getIdentifier().getName(), ctx, text));
+            }
+        }
+    };
+
+    public static final TextToASTTransformation TRANSFORMATION = new TextToASTTransformation();
+    private static final Logger LOG = LoggerFactory.getLogger(TextToASTTransformer.class);
+
+    private TextToASTTransformer(final SchemaRepository provider, final SchemaSourceRegistry consumer) {
+        super(provider, YangTextSchemaSource.class, consumer, ASTSchemaSource.class, TRANSFORMATION);
+    }
+
+    public static final TextToASTTransformer create(final SchemaRepository provider, final SchemaSourceRegistry consumer) {
+        return new TextToASTTransformer(provider, consumer);
+    }
+}
index 03dc0663de7ba5910a5f54bc294e1d09afe55cfe..a4360a5a67a93345f8e121781ae64ac4bee919c6 100644 (file)
@@ -24,7 +24,6 @@ import java.util.Date;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
@@ -110,7 +109,7 @@ public class GroupingTest {
         assertEquals("addresses reference added by refine", refineList.getReference());
         assertFalse(refineList.isConfiguration());
         assertEquals(2, (int) refineList.getConstraints().getMinElements());
-        assertEquals(12, (int) refineList.getConstraints().getMaxElements());
+        assertEquals(Integer.MAX_VALUE, (int) refineList.getConstraints().getMaxElements());
 
         // leaf id
         assertNotNull(refineInnerLeaf);
index 050f62092877a093f389eb8b12dca545749c1bc6..53c335b874350c4f5e6b3e40d733d009a3698a41 100644 (file)
@@ -16,6 +16,7 @@ import static org.junit.Assert.assertTrue;
 import java.io.File;
 import java.math.BigInteger;
 import java.net.URI;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Set;
 import org.junit.Before;
@@ -36,7 +37,7 @@ import org.opendaylight.yangtools.yang.model.util.BitsType;
 import org.opendaylight.yangtools.yang.model.util.EnumerationType;
 import org.opendaylight.yangtools.yang.model.util.ExtendedType;
 import org.opendaylight.yangtools.yang.model.util.IdentityrefType;
-import org.opendaylight.yangtools.yang.model.util.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.util.InstanceIdentifierType;
 import org.opendaylight.yangtools.yang.model.util.UnionType;
 
 public class TypesResolutionTest {
@@ -162,7 +163,7 @@ public class TypesResolutionTest {
     public void testInstanceIdentifier1() {
         Module tested = TestUtils.findModule(testedModules, "custom-types-test");
         LeafSchemaNode leaf = (LeafSchemaNode) tested.getDataChildByName("inst-id-leaf1");
-        InstanceIdentifier leafType = (InstanceIdentifier) leaf.getType();
+        InstanceIdentifierType leafType = (InstanceIdentifierType) leaf.getType();
         assertFalse(leafType.requireInstance());
         assertEquals(1, leaf.getUnknownSchemaNodes().size());
     }
@@ -171,7 +172,7 @@ public class TypesResolutionTest {
     public void testInstanceIdentifier2() {
         Module tested = TestUtils.findModule(testedModules, "custom-types-test");
         LeafSchemaNode leaf = (LeafSchemaNode) tested.getDataChildByName("inst-id-leaf2");
-        InstanceIdentifier leafType = (InstanceIdentifier) leaf.getType();
+        InstanceIdentifierType leafType = (InstanceIdentifierType) leaf.getType();
         assertTrue(leafType.requireInstance());
     }
 
@@ -333,4 +334,13 @@ public class TypesResolutionTest {
         assertNotNull(type);
     }
 
+    @Test
+    public void testUnionWithExt() throws Exception {
+        File extdef = new File(getClass().getResource("/types/union-with-ext/extdef.yang").toURI());
+        File unionbug = new File(getClass().getResource("/types/union-with-ext/unionbug.yang").toURI());
+        File inet = new File(getClass().getResource("/ietf/ietf-inet-types@2010-09-24.yang").toURI());
+        YangContextParser parser = new YangParserImpl();
+        parser.parseFiles(Arrays.asList(extdef, unionbug, inet));
+    }
+
 }
index 0769f4231c0a9eb3971b8045f2ef201fb138fa0c..2426e3c7d576136e283042068c77595ccefd2511 100644 (file)
@@ -186,7 +186,7 @@ module foo {
                     reference "addresses reference added by refine";
                     config false;
                     min-elements 2;
-                    max-elements 12;
+                    max-elements unbounded;
                 }
                 refine addresses/id {
                     description "id of address";
diff --git a/yang/yang-parser-impl/src/test/resources/types/union-with-ext/extdef.yang b/yang/yang-parser-impl/src/test/resources/types/union-with-ext/extdef.yang
new file mode 100644 (file)
index 0000000..68a568b
--- /dev/null
@@ -0,0 +1,13 @@
+module "extdef" {
+    yang-version 1;
+    namespace "urn:test:bug:extdef";
+    prefix "extdef";
+
+    revision 2012-04-16 {
+    }
+
+    extension "help" {
+        argument "text";
+    }
+
+}
diff --git a/yang/yang-parser-impl/src/test/resources/types/union-with-ext/unionbug.yang b/yang/yang-parser-impl/src/test/resources/types/union-with-ext/unionbug.yang
new file mode 100644 (file)
index 0000000..a882168
--- /dev/null
@@ -0,0 +1,31 @@
+module unionbug {
+    yang-version 1;
+    namespace "urn:test:bug:unionbug";
+    prefix "unionbug";
+
+    import extdef {
+        prefix extdef;
+    }
+
+    import ietf-inet-types {
+        prefix "inet";
+    }
+
+    revision 2012-04-16 {
+    }
+
+    typedef address {
+        type union {
+            type inet:ip-address {
+                extdef:help "IP address";
+            }
+            type inet:ip-prefix {
+                extdef:help "Subnet";
+            }
+            type string {
+                extdef:help "Address name";
+            }
+        }
+    }
+
+}